我们假设我有下表。 我怎样才能分别计算每个月的延误。
Req start_dt end_dt
1 1/2/2017 3/5/2017
2 5/2/2017 7/6/2017
我希望结果如下表所示。
Req start_dt end_dt delay MM
1 1/2/2017 3/5/2017 30 1
1 1/2/2017 3/5/2017 28 2
1 1/2/2017 3/5/2017 5 3
2 5/2/2017 7/6/2017 30 5
2 5/2/2017 7/6/2017 30 6
2 5/2/2017 7/6/2017 6 7
答案 0 :(得分:2)
您需要将日期范围拆分为多个月,中间月份的完整范围和开始和结束月份的部分范围。一种方法是使用recursive subquery factoring
with rcte (req, start_dt, end_dt, period_start_dt, period_end_dt) as (
select req, start_dt, end_dt, start_dt,
case when trunc(end_dt, 'MM') = trunc(start_dt, 'MM') then end_dt
else last_day(start_dt) end
from your_table
union all
select req, start_dt, end_dt, add_months(trunc(period_start_dt, 'MM'), 1),
case when trunc(end_dt, 'MM') = add_months(trunc(period_start_dt, 'MM'), 1) then end_dt
else last_day(add_months(trunc(period_start_dt, 'MM'), 1) ) end
from rcte
where trunc(end_dt, 'MM') > trunc(period_start_dt, 'MM')
)
select req, start_dt, end_dt, period_start_dt, period_end_dt,
period_end_dt - period_start_dt + 1 as delay,
extract(month from period_start_dt) as mm
from rcte
order by req, period_start_dt, period_end_dt;
REQ START_DT END_DT PERIOD_STA PERIOD_END DELAY MM
---------- ---------- ---------- ---------- ---------- ---------- ----------
1 2017-01-02 2017-03-05 2017-01-02 2017-01-31 30 1
1 2017-01-02 2017-03-05 2017-02-01 2017-02-28 28 2
1 2017-01-02 2017-03-05 2017-03-01 2017-03-05 5 3
2 2017-05-02 2017-07-06 2017-05-02 2017-05-31 30 5
2 2017-05-02 2017-07-06 2017-06-01 2017-06-30 30 6
2 2017-05-02 2017-07-06 2017-07-01 2017-07-06 6 7
递归CTE有一个锚点成员,它从表中获取初始数据,并计算第一个周期的开始和结束。期间开始是原始范围开始日期;期末是范围结束日期(如果它在同一个月内)或该月末。
递归成员然后使用锚点中的值并生成一个新的句点,该句点将从下个月的开始开始,并再次在原始范围结束日期或该月末结束。
一旦您有了这些期间的开始/结束日期,使用正常日期减法计算差异就很简单了。 (我已经在输出中留下了句号的开始/结束日期以尝试使其更清晰;如果您不想要它们,只需从最终选择列表中删除。)
期间开始/结束日期的计算稍微简单:
with rcte (req, start_dt, end_dt, period_start_dt, period_end_dt) as (
select req, start_dt, end_dt, start_dt, least(end_dt, last_day(start_dt))
from your_table
union all
select req, start_dt, end_dt, add_months(trunc(period_start_dt, 'MM'), 1),
least(end_dt, last_day(add_months(trunc(period_start_dt, 'MM'), 1)))
from rcte
where trunc(end_dt, 'MM') > trunc(period_start_dt, 'MM')
)
select req, start_dt, end_dt,
period_end_dt - period_start_dt + 1 as delay,
extract(month from period_start_dt) as mm
from rcte
order by req, period_start_dt, period_end_dt;
REQ START_DT END_DT DELAY MM
---------- ---------- ---------- ---------- ----------
1 2017-01-02 2017-03-05 30 1
1 2017-01-02 2017-03-05 28 2
1 2017-01-02 2017-03-05 5 3
2 2017-05-02 2017-07-06 30 5
2 2017-05-02 2017-07-06 30 6
2 2017-05-02 2017-07-06 6 7
答案 1 :(得分:2)
您可以使用相关的分层查询来执行此操作:
Oracle 11g R2架构设置:
CREATE TABLE table_name ( Req, start_dt, end_dt ) AS
SELECT 1, DATE '2017-01-02', DATE '2017-03-05' FROM DUAL UNION ALL
SELECT 2, DATE '2017-05-02', DATE '2017-07-06' FROM DUAL;
查询1 :
SELECT t.*,
LEAST( LAST_DAY( d.COLUMN_VALUE ), t.end_dt )
- GREATEST( d.COLUMN_VALUE, t.start_dt ) + 1 AS delay,
EXTRACT( MONTH FROM d.COLUMN_VALUE ) AS MM
FROM table_name t
CROSS JOIN
TABLE(
CAST(
MULTISET(
SELECT ADD_MONTHS( TRUNC( t.start_dt, 'MM' ), LEVEL - 1 )
FROM DUAL
CONNECT BY LEVEL <= MONTHS_BETWEEN( t.end_dt, TRUNC( t.start_dt, 'MM' ) ) + 1
) AS SYS.ODCIDATELIST
)
) d
<强> Results 强>:
| REQ | START_DT | END_DT | DELAY | MM |
|-----|----------------------|----------------------|-------|----|
| 1 | 2017-01-02T00:00:00Z | 2017-03-05T00:00:00Z | 30 | 1 |
| 1 | 2017-01-02T00:00:00Z | 2017-03-05T00:00:00Z | 28 | 2 |
| 1 | 2017-01-02T00:00:00Z | 2017-03-05T00:00:00Z | 5 | 3 |
| 2 | 2017-05-02T00:00:00Z | 2017-07-06T00:00:00Z | 30 | 5 |
| 2 | 2017-05-02T00:00:00Z | 2017-07-06T00:00:00Z | 30 | 6 |
| 2 | 2017-05-02T00:00:00Z | 2017-07-06T00:00:00Z | 6 | 7 |