Teradata - 将日期范围拆分为带有天数的月份列

Teradata - Split date range into month columns with day count

我需要将一个季度内的不同日期范围拆分为月列,其中仅包含该月实际使用的天数。每条记录(范围)都会不同。

例子:

Table

Record_ID Start_Date  End_Date
1         10/27       11/30
2         11/30       12/14
3         12/14       12/31

Range 1 = 10/5 to 12/14
Range 2 = 11/20 to 12/31
Range 3 = 10/28 to 12/2

输出:

范围 1

Oct    Nov    Dec 
27     30     14

首先加入日历获取范围内的所有日期并获取每个月的天数(包括完整的月份,未在Start_Date和End_Date中提及)。

然后在每个范围的列中总结每个月。

create table SplitDateRange ( Range bigint, Start_Date date,  End_Date date );
insert into SplitDateRange values ( 1, '2018-10-05', '2018-12-14' );
insert into SplitDateRange values ( 2, '2018-11-20', '2018-12-31' );
insert into SplitDateRange values ( 3, '2018-10-28', '2018-12-02' );

select
          Range
        , sum(case when mon = 10 then days else 0 end) as "Oct"
        , sum(case when mon = 11 then days else 0 end) as "Nov"
        , sum(case when mon = 12 then days else 0 end) as "Dec"
from (
        select
                Range
                , extract(MONTH from C.calendar_date) as mon
                , max(C.calendar_date) - min(calendar_date) +1 as days
        from Sys_Calendar.CALENDAR as C
        inner join SplitDateRange as DR
        on C.calendar_date between DR.Start_Date and DR.End_Date
        group by 1,2
) A
group by Range
order by Range
;

类似于@ULick 使用sys_calendar.calendar 的回答,但更加简洁:

CREATE VOLATILE MULTISET TABLE datetest (record_id int, start_date date, end_date date) ON COMMIT PRESERVE ROWS;

INSERT INTO datetest VALUES (1, '2017-10-05', '2017-12-14');
INSERT INTO datetest VALUES (2, '2017-11-20','2017-12-31');

SELECT record_id, 
    SUM(CASE WHEN month_of_year = 10 THEN 1 ELSE 0 END) as October,
    SUM(CASE WHEN month_of_year = 11 THEN 1 ELSE 0 END) as November,
    SUM(CASE WHEN month_of_year = 12 THEN 1 ELSE 0 END) as December
FROM datetest
    INNER JOIN sys_calendar.calendar cal 
        ON cal.calendar_date BETWEEN start_date and end_date
GROUP BY record_id;

DROP TABLE datetest;

因为问题中提到了四分之一(我不确定它在这里有什么关系)所以 sys_calendar 中还有 quarter_of_yearmonth_of_quarter 可以用来切分这个更进一步。

此外,如果您使用的是 16.00+,则有 PIVOT 功能可能有助于摆脱此处的 CASE 语句。

不同的方法,通过应用 Teradata Expand On 功能来创建时间序列,避免交叉连接到日历。更多文本,但对于更大的文本应该更有效 tables/ranges:

SELECT record_id,
   Sum(CASE WHEN mth = 10 THEN days_in_month ELSE 0 END) AS October,
   Sum(CASE WHEN mth = 11 THEN days_in_month ELSE 0 END) AS November,
   Sum(CASE WHEN mth = 12 THEN days_in_month ELSE 0 END) AS December
FROM 
 ( -- this Derived Table simply avoids repeating then EXTRACT/INTERVAL calculations (can't be done directly in the nested Select)
   SELECT record_id,
      Extract(MONTH From Begin(expanded_pd)) AS mth, 
      Cast((INTERVAL( base_pd P_INTERSECT expanded_pd) DAY) AS INT) AS days_in_month
   FROM 
    (
      SELECT record_id,
         PERIOD(start_date, end_date+1) AS base_pd, 
         expanded_pd
      FROM datetest
        -- creates one row per month
      EXPAND ON base_pd AS expanded_pd BY ANCHOR PERIOD Month_Begin
    ) AS dt
 ) AS dt
GROUP BY 1