如何生成具有开始日期和结束日期的动态 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 来欺骗 UNION
。 EXPAND 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
我想生成一个动态 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 来欺骗 UNION
。 EXPAND 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