2

我有一张包含如下数据的表格

Group       Start Date        End Date
A        01/01/01       01/03/01
A       01/01/01        01/02/01
A       01/03/01        01/04/01
B       01/01/01        01/01/01
ETC

我希望生成一个每天计数的视图,就像这样

Group       Date        Count
A       01/01/01            2
A       01/02/01            2
A       01/03/01            2
A       01/04/01         1
B       01/01/01            1

我正在使用 Oracle 9,完全不知道如何处理这个问题,我正在寻找任何让我开始的想法。
注意:生成一个表格来保存日期是不切实际的,因为我的最终产品必须分解到分钟。

4

3 回答 3

3
WITH    q AS
        (
        SELECT  (
                SELECT  MIN(start_date)
                FROM    mytable
                ) + level - 1 AS mydate
        FROM    dual
        CONNECT BY
                level <= (
                SELECT  MAX(end_date) - MIN(start_date)
                FROM    mytable
                )
        )
SELECT  group, mydate,
        (
        SELECT  COUNT(*)
        FROM    mytable mi
        WHERE   mi.group = mo.group
                AND q BETWEEN mi.start_date AND mi.end_date
        ) 
FROM    q
CROSS JOIN
        (
        SELECT  DISTINCT group
        FROM    mytable
        ) mo

更新:

使用分析函数的更好更快的查询。

主要思想是包含每个日期的范围数是在该日期之前开始的范围计数与在该日期之前结束的范围计数之前的差异。

SELECT  cur_date,
        grouper,
        SUM(COALESCE(scnt, 0) - COALESCE(ecnt, 0)) OVER (PARTITION BY grouper ORDER BY cur_date) AS ranges
FROM    (
        SELECT  (
                SELECT  MIN(start_date)
                FROM    t_range
                ) + level - 1 AS cur_date
        FROM    dual
        CONNECT BY
                level <=
                (
                SELECT  MAX(end_date)
                FROM    t_range
                ) -
                (
                SELECT  MIN(start_date)
                FROM    t_range
                ) + 1
        ) dates
CROSS JOIN
        (
        SELECT  DISTINCT grouper AS grouper
        FROM    t_range
        ) groups
LEFT JOIN
        (
        SELECT  grouper AS sgrp, start_date, COUNT(*) AS scnt
        FROM    t_range
        GROUP BY
                grouper, start_date
        ) starts
ON      sgrp = grouper
        AND start_date = cur_date
LEFT JOIN
        (
        SELECT  grouper AS egrp, end_date, COUNT(*) AS ecnt
        FROM    t_range
        GROUP BY
                grouper, end_date
        ) ends
ON      egrp = grouper
        AND end_date = cur_date - 1
ORDER BY
        grouper, cur_date

此查询在1第二1,000,000行完成。

有关更多详细信息,请参阅我的博客中的此条目:

于 2009-09-08T14:01:22.700 回答
2

您可以使用这些 SO 中描述的方法:

基本上:加入生成的日历和 GROUP BY 您的列子集。

SQL> WITH DATA AS (
  2  SELECT 'A' grp, to_date('01/01/01') start_date, to_date('01/03/01') end_date FROM DUAL
  3  UNION ALL SELECT 'A', to_date('01/01/01'), to_date('01/02/01') FROM DUAL
  4  UNION ALL SELECT 'A', to_date('01/03/01'), to_date('01/04/01') FROM DUAL
  5  UNION ALL SELECT 'B', to_date('01/01/01'), to_date('01/01/01') FROM DUAL
  6  ), calendar AS (
  7  SELECT to_date('01/01/01') + ROWNUM - 1 d
  8    FROM dual
  9    CONNECT BY LEVEL <= to_date('01/04/01') - to_date('01/01/01') + 1
 10  )
 11  SELECT data.grp, calendar.d, COUNT(*) cnt
 12    FROM data
 13    JOIN calendar ON calendar.d BETWEEN data.start_date AND data.end_date
 14   GROUP BY data.grp, calendar.d;

GRP D                  CNT
--- ----------- ----------
A   04/01/2001           1
A   02/01/2001           2
B   01/01/2001           1
A   03/01/2001           2
A   01/01/2001           2
于 2009-09-08T13:54:43.973 回答
1

通常我用一个数字表来解决这种问题:

WITH Dates AS (
    SELECT DateAdd(d, Numbers.Number - 1, '1/1/2001') AS Date
    FROM Numbers
    WHERE Numbers.Number BETWEEN 1 AND 100000 -- Arbitrary date range
)
SELECT GroupData.Group, Dates.Date, COUNT(*)
FROM Dates
LEFT JOIN GroupData
    ON Dates.Date BETWEEN GroupData.StartDate AND GroupData.EndDate
GROUP BY GroupData.Group, Dates.Date
ORDER BY GroupData.Group, Dates.Date
于 2009-09-08T13:57:38.880 回答