按月计算日期范围的日历天数

Calculate calendar days by month for date ranges

我有一个 table 日期范围:

CREATE TABLE REQUEST (
    REQUEST_ID NUMBER(*,0) NOT NULL ENABLE,
    EMPLOYEE_ID NUMBER(*,0) NOT NULL ENABLE,
    START_DATE DATE NOT NULL ENABLE,
    END_DATE DATE NOT NULL ENABLE,
    CONSTRAINT REQUEST_PK PRIMARY KEY (REQUEST_ID)
);

还有一些限制条件(为简洁起见省略)确保它们有效(结束日期不能小于开始日期)并强制时间为 00:00:00。 (Runnable fiddle with sample data).

有没有办法根据日期范围将 year/month 的数据集拆分为结果集?规则是:

例如,带有 [2020-12-28, 2021-02-10] 的请求将产生三行:

request_id year month days
========== ==== ===== ====
         1 2020    12    4
         1 2021     1   31
         1 2021     2   10

我已经 playing with CONNECT BY 但我无法使其适应我的用例。这是正确的工具吗?

例如这个:

WITH t AS (
    SELECT DATE '2020-12-28' +LEVEL-1 AS ts
    FROM dual 
    CONNECT BY  DATE '2020-12-28' +LEVEL-1 <= DATE '2021-02-10')
SELECT 
    EXTRACT(YEAR FROM TRUNC(ts, 'Month')) AS YEAR, 
    EXTRACT(MONTH FROM TRUNC(ts, 'Month')) AS MONTH, 
    COUNT(ts) AS DAYS 
FROM t
GROUP BY TRUNC(ts, 'Month')
ORDER BY YEAR, MONTH;

一种更直接的计算方法是使用 connect by 只生成所需的月份(不是每个时间间隔的每一天)- 然后直接进行天计算,而不是通过计数。像这样:

添加测试数据:

insert into request (request_id, employee_id, start_date, end_date)
    select 1, 1001, date '2020-12-28', date '2021-02-10' from dual union all
    select 2, 4002, date '2021-02-10', date '2021-02-20' from dual union all
    select 3, 6004, date '2020-12-15', date '2021-03-31' from dual
;

commit;

查询和输出:

with
  prep (request_id, start_date, end_date, mth) as (
    select  request_id, start_date, end_date, 
            add_months(trunc(start_date, 'mm'), level - 1)
    from    request
    connect by  level <= months_between(trunc(end_date, 'mm'),
                                        trunc(start_date, 'mm')) + 1
            and prior request_id = request_id
            and prior sys_guid() is not null
  )
select request_id, extract(year from mth) as year_,
       extract(month from mth) as month_,
       least(last_day(mth), end_date) - greatest(mth, start_date) + 1 as days
from   prep
order  by request_id, mth  --  if needed
;


REQUEST_ID      YEAR_     MONTH_       DAYS
---------- ---------- ---------- ----------
         1       2020         12          4
         1       2021          1         31
         1       2021          2         10
         2       2021          2         11
         3       2020         12         17
         3       2021          1         31
         3       2021          2         28
         3       2021          3         31