日期之间的月份计数

时间:2018-10-22 19:38:07

标签: sql oracle date

我有下表。我需要计算给定月份中有多少个活动ID。因此,我想为该月内活动的每个ID创建一行,以便可以每月计算ID。该月应为term_dt生成一行。

 active_dt    term_dt      id
 1/1/2018                  101
 1/1/2018   5/15/2018      102
 3/1/2018   6/1/2018       103
 1/1/2018   4/25/18        104

3 个答案:

答案 0 :(得分:2)

显然,这是一个“重叠间隔数”问题。算法如下:

  • 创建所有起点和终点的排序列表
  • 计算此列表上的连续总和,遇到开始时加一,遇到结束时减一
    • 如果两点相同,则先进行减法
  • 您最终将获得总和更改的所有点的列表

这是查询的大致轮廓。它用于SQL Server,但可以移植到支持窗口功能的任何RDBMS:

WITH cte1(date, val) AS (
    SELECT active_dt, 1 FROM @t AS t
    UNION ALL
    SELECT COALESCE(term_dt, '2099-01-01'), -1 FROM @t AS t
    -- if end date is null then assume the row is valid indefinitely
), cte2 AS (
    SELECT date, SUM(val) OVER(ORDER BY date, val) AS rs
    FROM cte1
)
SELECT YEAR(date) AS YY, MONTH(date) AS MM, MAX(rs) AS MaxActiveThisYearMonth
FROM cte2
GROUP BY YEAR(date), MONTH(date)

DB Fiddle

答案 1 :(得分:0)

我想用一个更简单的查询来解决这个问题,对于Oracle来说似乎可以解决问题:

with candidates (month_start) as (
    select to_date ('2018-' || column_value || '-01','YYYY-MM-DD') 
    from 
      table 
       (sys.odcivarchar2list('01','02','03','04','05',
                             '06','07','08','09','10','11','12'))
),  sample_data (active_dt, term_dt, id) as (
  select to_date('01/01/2018', 'MM/DD/YYYY'), null,  101 from dual
  union select to_date('01/01/2018', 'MM/DD/YYYY'), 
               to_date('05/15/2018', 'MM/DD/YYYY'), 102 from dual
  union select to_date('03/01/2018', 'MM/DD/YYYY'), 
               to_date('06/01/2018', 'MM/DD/YYYY'), 103 from dual 
  union select to_date('01/01/2018', 'MM/DD/YYYY'), 
               to_date('04/25/2018', 'MM/DD/YYYY'), 104 from dual
)
select c.month_start, count(1) 
from candidates c
join sample_data d 
  on c.month_start between d.active_dt and nvl(d.term_dt,current_date)
group by c.month_start
order by c.month_start

答案 2 :(得分:0)

另一种解决方案是使用分层查询,例如:

WITH your_table AS (SELECT to_date('01/01/2018', 'dd/mm/yyyy') active_dt, NULL term_dt, 101 ID FROM dual UNION ALL
                    SELECT to_date('01/01/2018', 'dd/mm/yyyy') active_dt, to_date('15/05/2018', 'dd/mm/yyyy') term_dt, 102 ID FROM dual UNION ALL
                    SELECT to_date('01/03/2018', 'dd/mm/yyyy') active_dt, to_date('01/06/2018', 'dd/mm/yyyy') term_dt, 103 ID FROM dual UNION ALL
                    SELECT to_date('01/01/2018', 'dd/mm/yyyy') active_dt, to_date('25/04/2018', 'dd/mm/yyyy') term_dt, 104 ID FROM dual)
SELECT active_month,
       COUNT(*) num_active_ids
FROM   (SELECT add_months(TRUNC(active_dt, 'mm'), -1 + LEVEL) active_month,
               ID
        FROM   your_table
        CONNECT BY PRIOR ID = ID
                   AND PRIOR sys_guid() IS NOT NULL
                   AND LEVEL <= FLOOR(months_between(coalesce(term_dt, SYSDATE), active_dt)) + 1)
GROUP BY active_month
ORDER BY active_month;

ACTIVE_MONTH NUM_ACTIVE_IDS
------------ --------------
01/01/2018                3
01/02/2018                3
01/03/2018                4
01/04/2018                4
01/05/2018                3
01/06/2018                2
01/07/2018                1
01/08/2018                1
01/09/2018                1
01/10/2018                1

这是比其他答案或多或少的性能取决于您的测试。