【问题标题】:Teradata - Split date range into month columns with day countTeradata - 将日期范围拆分为具有天数的月份列
【发布时间】:2018-02-20 16:35:40
【问题描述】:

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

示例

表格

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

【问题讨论】:

  • 桌子长什么样?
  • 只有:Record_ID、Start_Date、End_Date
  • 开始和结束之间的时间段是否跨越到下一年(如果是,将如何显示)。开始和结束之间的时间段是否超过 1 年(再次在这里如何显示)?
  • 您的范围都重叠,我不知道您的输出代表什么。你到底想做什么?

标签: sql teradata


【解决方案1】:

类似于@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;

因为在问题中提到了 Quarter(我不确定它与这里的关系),所以 sys_calendar 中还有 quarter_of_yearmonth_of_quarter 可用于进一步切片和切块。

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

【讨论】:

  • 谢谢!工作完美。正是我需要的,我只是看不到它。
【解决方案2】:

先加入日历,获取范围内的所有日期,获取每个月的天数(包括整月,Start_Date和End_Date中没有提到)。

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

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
;

【讨论】:

  • 编辑后,我不明白,第一个(添加的)表与预期结果有什么关系。
【解决方案3】:

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

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

【讨论】:

  • 谢谢dnoeth!我真的很感激。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-02-28
  • 1970-01-01
  • 1970-01-01
  • 2018-06-06
  • 1970-01-01
  • 1970-01-01
  • 2020-02-28
相关资源
最近更新 更多