如何生成具有开始日期和结束日期的动态 table Teradata 或 SAS SQL

How to generate dynamic table having begin month date and end month date Teradata or SAS SQL

我想生成一个动态 Table 以月份的开始日期作为一列,以月份的结束日期作为另一列。

理想情况下,我想提供两年,f.e。 2016 年和 2021 年。提供这两年时我希望得到的输出如下:

Begin_of_Month End_of_Month
2016-01-01     2016-01-31
2016-02-01     2016-02-29
.
.
.
2021-12-01     2021-12-31

请注意,我需要从 2016 年到 2021 年的所有年份的输出。在我上面的示例中,这意味着应该包括 2017 2018 2019 2020。

我试过使用 Teradata 的时间序列函数,但没有得到结果。

我尝试在 Teradata 中重新创建的解决方案是:

另外,我试过Teradata的EXPAND ON PERIOD时序功能

我敢肯定有一些奇特的方法可以做到这一点,但我认为只需点击内置日历 table 可能是最简单的方法:

SELECT DISTINCT 
    min(calendar_date) OVER (PARTITION BY year_of_calendar, month_of_calendar) as start_of_month, 
    max(calendar_date) OVER (PARTITION BY year_of_calendar, month_of_calendar) as end_of_month  
FROM sys_calendar.calendar
WHERE year_of_calendar BETWEEN 2016 and 2021

在没有 table 引用的情况下执行此操作会变得有点难看。 EXPAND ON 似乎是一条明显的路线,但如果 FROM 子句中没有 table 引用,它就会出错。 UNION 遇到同样的问题,但我们可以通过使用 cte 来欺骗 UNIONEXPAND ON 更挑剔,为了欺骗它,我们可以劫持 Teradata 的 JSON_TABLE 功能:

SELECT BEGIN(dt), PRIOR(END(dt))
FROM JSON_TABLE
    ( 
        ON (SELECT 1 as id, NEW JSON('{"startdate":"2016-01-01","enddate":"2021-12-31"}') jd)
        USING 
            rowexpr('$')
            colexpr('[{"jsonpath" : "$.startdate", "type" : "DATE"},
                      {"jsonpath" : "$.enddate", "type" : "DATE"}]')
    ) as jt(id, startdate, enddate)
EXPAND ON PERIOD(startdate, enddate) as dt BY ANCHOR MONTH_BEGIN

您也可以使用递归 CTE 来构建月份,这感觉不那么棘手,但生成时间更长。

WITH startend AS
(
    SELECT 
        DATE '2016-01-01' periodstartdate,
        DATE '2021-12-31' AS periodenddate
)
,RECURSIVE months AS
(
    SELECT periodstartdate,
        periodenddate,
        periodstartdate as monthstartdate,
        1 as monthoffset
    FROM startend
    UNION ALL
    SELECT periodstartdate,
        periodenddate,
        ADD_MONTHS(periodstartdate, monthoffset),
        monthoffset + 1
    FROM 
        months 
    WHERE monthoffset < months_between(periodenddate, periodstartdate) 
)
SELECT monthstartdate, monthstartdate + INTERVAL '1' MONTH - INTERVAL '1' DAY as monthenddate from months;

如果有更优雅的方法来实现这一点,我将非常感兴趣。没有 dual 或像其他 RDBMS 中那样的序列生成,构建没有 table 引用的数据集的选项非常有限。

如果您打算在 SAS 中执行此操作,则不需要 SQL。

data want;
  do year=2016 to 2021;
    do month=1 to 12;
      start_of_month=mdy(month,1,year);
      end_of_month=intnx('month',start_of_month,0,'e');
      output;
    end;
  end;
  format start_of_month end_of_month yymmdd10.;
  drop year month;
run;

通常只有在 FROM 中访问 table 时 EXPAND ON 才有效,但是应用某些函数如 TRUNC 或 TO_DATE 会愚弄优化器:

WITH dummy AS 
 (
   SELECT 
      2016 AS yr_start
     ,2021 as yr_end
     ,TO_DATE(TRIM(yr_start) || '-01-01') AS pd_start
     ,TO_DATE(TRIM(yr_end+1) || '-01-01') AS pd_end
 ) 
SELECT 
   BEGIN(pd) AS Begin_of_Month
  ,LAST(pd)  AS End_of_Month
FROM dummy
EXPAND ON PERIOD(pd_start, pd_end) AS pd
          BY INTERVAL '1' MONTH