代码之家  ›  专栏  ›  技术社区  ›  Will Dieterich

统计列日期范围内每个日期发生的行数

  •  1
  • Will Dieterich  · 技术社区  · 15 年前

    我有一张表,上面有如下数据

    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
    

    我使用的是Oracle9,在如何处理这一问题上完全迷失了方向,我正在寻找任何让我开始工作的想法。
    注意:生成一个表来保存日期是不实际的,因为我的最终产品必须分解到分钟。

    3 回复  |  直到 15 年前
        1
  •  1
  •   Cade Roux    15 年前

    通常我用数字表来解决这类问题:

    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
    
        2
  •  3
  •   Quassnoi    15 年前
    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 排。

    有关详细信息,请参阅我的日志:

        3
  •  2
  •   Community Tales Farias    7 年前

    您可以使用下面描述的方法,以便:

    基本上:加入一个生成的日历并按列的子集分组。

    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