开始日期结束日期合并行
start date end date combine rows
在 Redshift 中,通过 SQL 脚本想要合并月度记录,只要第一个记录的结束日期和下一个记录的开始日期之间的间隔为 32 天或更短 (<=32) 为单个记录连续月份的最小开始日期作为输出开始日期,连续月份的最大结束日期作为输出结束日期。
下面的输入数据是指 table 的数据,还列出了预期的输出。输入数据已列出 ORDER BY ID,STARTDT,ENDDT in ASC
。
例如,在下面的 table 中,考虑 ID 100,第一个记录的结尾和下一个记录的开始之间的间隔 <=32,但是第二个记录的结束日期和第三个记录之间的间隔开始日期超过 32 天,因此前两条记录合并为一条记录,即 (ID),MIN(STARTSDT),MAX(ENDDT)
,它对应于预期输出中的第一条记录。类似地,输入数据中的 3 到 4 条记录落在 32 天内,因此这 2 条记录将合并为单个记录,对应于预期输出中的第二条记录。
输入数据:
ID STARTDT ENDDT
100 2000-01-01 2000-01-31
100 2000-02-01 2000-02-29
100 2000-05-01 2000-05-31
100 2000-06-01 2000-06-30
100 2000-09-01 2000-09-30
100 2000-10-01 2000-10-31
101 2012-06-01 2012-06-30
101 2012-07-01 2012-07-31
102 2000-01-01 2000-01-31
103 2013-03-01 2013-03-31
103 2013-05-01 2013-05-31
预期输出:
ID MIN_STARTDT MAX_END_DT
100 2000-01-01 2000-02-29
100 2000-05-01 2000-06-30
100 2000-09-01 2000-10-31
101 2012-06-01 2012-07-31
102 2000-01-01 2000-01-31
103 2013-03-01 2013-03-31
103 2013-05-01 2013-05-31
您可以按以下步骤执行此操作:
- 使用
join
确定两条相邻记录应合并的位置。
- 然后做一个累加和,为所有这些相邻的记录分配一个分组标识符。
- 汇总。
看起来像:
select id, min(startdt), max(enddte)
from (select t.*,
count(case when tprev.id is null then 1 else 0 end) over
(partition by t.idid
order by t.startdt
rows between unbounded preceding and current row
) as grp
from t left join
t tprev
on t.id = tprev.id and
t.startdt = tprev.enddt + interval '1 day'
) t
group by id, grp;
这个问题和这个很相似,我的回答也很相似:Fetch rows based on condition
这个想法的要点是使用Window函数来识别周期之间的转换(相隔小于33天的事件),然后进行一些过滤以删除周期内的行,然后Window 再次运行。
完整的解决方案:
SELECT
id,
startdt AS period_start,
period_end
FROM (
SELECT
id,
startdt,
enddt,
lead(enddt, 1)
OVER (PARTITION BY id
ORDER BY enddt) AS period_end,
period_boundary
FROM (
SELECT
id,
startdt,
enddt,
CASE WHEN period_switch = 0 AND reverse_period_switch = 1
THEN 'start'
ELSE 'end' END AS period_boundary
FROM (
SELECT
id,
startdt,
enddt,
CASE WHEN datediff(days, enddt, lead(startdt, 1)
OVER (PARTITION BY id
ORDER BY enddt ASC)) > 32
THEN 1
ELSE 0 END AS period_switch,
CASE WHEN datediff(days, lead(enddt, 1)
OVER (PARTITION BY id
ORDER BY enddt DESC), startdt) > 32
THEN 1
ELSE 0 END AS reverse_period_switch
FROM date_test
)
AS sessioned
WHERE period_switch != 0 OR reverse_period_switch != 0
UNION
SELECT -- adding start rows without transition
id,
startdt,
enddt,
'start'
FROM (
SELECT
id,
startdt,
enddt,
row_number()
OVER (PARTITION BY id
ORDER BY enddt ASC) AS row_num
FROM date_test
) AS with_row_number
WHERE row_num = 1
UNION
SELECT -- adding end rows without transition
id,
startdt,
enddt,
'end'
FROM (
SELECT
id,
startdt,
enddt,
row_number()
OVER (PARTITION BY id
ORDER BY enddt desc) AS row_num
FROM date_test
) AS with_row_number
WHERE row_num = 1
) AS with_boundary -- data set containing start/end boundaries
) AS with_end -- data set where end date is propagated into the start row of the period
WHERE period_boundary = 'start'
ORDER BY id, startdt ASC;
请注意,在您的预期输出中,您有一行 103 2013-05-01 2013-05-31
,但它的开始日期与前一行的结束日期相差 31 天,所以根据您的要求,这一行应该与 id 103
的前一行合并。
所以我得到的输出如下所示:
id start end
100 2000-01-01 2000-02-29
100 2000-05-01 2000-06-30
100 2000-09-01 2000-10-31
101 2012-06-01 2012-07-31
102 2000-01-01 2000-01-31
103 2013-03-01 2013-05-31
在 Redshift 中,通过 SQL 脚本想要合并月度记录,只要第一个记录的结束日期和下一个记录的开始日期之间的间隔为 32 天或更短 (<=32) 为单个记录连续月份的最小开始日期作为输出开始日期,连续月份的最大结束日期作为输出结束日期。
下面的输入数据是指 table 的数据,还列出了预期的输出。输入数据已列出 ORDER BY ID,STARTDT,ENDDT in ASC
。
例如,在下面的 table 中,考虑 ID 100,第一个记录的结尾和下一个记录的开始之间的间隔 <=32,但是第二个记录的结束日期和第三个记录之间的间隔开始日期超过 32 天,因此前两条记录合并为一条记录,即 (ID),MIN(STARTSDT),MAX(ENDDT)
,它对应于预期输出中的第一条记录。类似地,输入数据中的 3 到 4 条记录落在 32 天内,因此这 2 条记录将合并为单个记录,对应于预期输出中的第二条记录。
输入数据:
ID STARTDT ENDDT
100 2000-01-01 2000-01-31
100 2000-02-01 2000-02-29
100 2000-05-01 2000-05-31
100 2000-06-01 2000-06-30
100 2000-09-01 2000-09-30
100 2000-10-01 2000-10-31
101 2012-06-01 2012-06-30
101 2012-07-01 2012-07-31
102 2000-01-01 2000-01-31
103 2013-03-01 2013-03-31
103 2013-05-01 2013-05-31
预期输出:
ID MIN_STARTDT MAX_END_DT
100 2000-01-01 2000-02-29
100 2000-05-01 2000-06-30
100 2000-09-01 2000-10-31
101 2012-06-01 2012-07-31
102 2000-01-01 2000-01-31
103 2013-03-01 2013-03-31
103 2013-05-01 2013-05-31
您可以按以下步骤执行此操作:
- 使用
join
确定两条相邻记录应合并的位置。 - 然后做一个累加和,为所有这些相邻的记录分配一个分组标识符。
- 汇总。
看起来像:
select id, min(startdt), max(enddte)
from (select t.*,
count(case when tprev.id is null then 1 else 0 end) over
(partition by t.idid
order by t.startdt
rows between unbounded preceding and current row
) as grp
from t left join
t tprev
on t.id = tprev.id and
t.startdt = tprev.enddt + interval '1 day'
) t
group by id, grp;
这个问题和这个很相似,我的回答也很相似:Fetch rows based on condition
这个想法的要点是使用Window函数来识别周期之间的转换(相隔小于33天的事件),然后进行一些过滤以删除周期内的行,然后Window 再次运行。
完整的解决方案:
SELECT
id,
startdt AS period_start,
period_end
FROM (
SELECT
id,
startdt,
enddt,
lead(enddt, 1)
OVER (PARTITION BY id
ORDER BY enddt) AS period_end,
period_boundary
FROM (
SELECT
id,
startdt,
enddt,
CASE WHEN period_switch = 0 AND reverse_period_switch = 1
THEN 'start'
ELSE 'end' END AS period_boundary
FROM (
SELECT
id,
startdt,
enddt,
CASE WHEN datediff(days, enddt, lead(startdt, 1)
OVER (PARTITION BY id
ORDER BY enddt ASC)) > 32
THEN 1
ELSE 0 END AS period_switch,
CASE WHEN datediff(days, lead(enddt, 1)
OVER (PARTITION BY id
ORDER BY enddt DESC), startdt) > 32
THEN 1
ELSE 0 END AS reverse_period_switch
FROM date_test
)
AS sessioned
WHERE period_switch != 0 OR reverse_period_switch != 0
UNION
SELECT -- adding start rows without transition
id,
startdt,
enddt,
'start'
FROM (
SELECT
id,
startdt,
enddt,
row_number()
OVER (PARTITION BY id
ORDER BY enddt ASC) AS row_num
FROM date_test
) AS with_row_number
WHERE row_num = 1
UNION
SELECT -- adding end rows without transition
id,
startdt,
enddt,
'end'
FROM (
SELECT
id,
startdt,
enddt,
row_number()
OVER (PARTITION BY id
ORDER BY enddt desc) AS row_num
FROM date_test
) AS with_row_number
WHERE row_num = 1
) AS with_boundary -- data set containing start/end boundaries
) AS with_end -- data set where end date is propagated into the start row of the period
WHERE period_boundary = 'start'
ORDER BY id, startdt ASC;
请注意,在您的预期输出中,您有一行 103 2013-05-01 2013-05-31
,但它的开始日期与前一行的结束日期相差 31 天,所以根据您的要求,这一行应该与 id 103
的前一行合并。
所以我得到的输出如下所示:
id start end
100 2000-01-01 2000-02-29
100 2000-05-01 2000-06-30
100 2000-09-01 2000-10-31
101 2012-06-01 2012-07-31
102 2000-01-01 2000-01-31
103 2013-03-01 2013-05-31