在 PostgreSQL 中的两个日期之间的每个月创建一行
Create a row for every month between 2 dates in PostgreSQL
我需要为 table 中每个人的 2 个日期之间的每个月创建一行(结果应该是该月的第一天)。例如,如果我的源 table:
中有以下数据
rowID | person | startdate | enddate
1 | 12345 | 2014-04-01 | 2014-11-30
2 | 67890 | 2014-03-01 | 2014-05-01
我希望我的目的地 table 的结果是:
person | month
12345 | 2014-04-01
12345 | 2014-05-01
12345 | 2014-06-01
12345 | 2014-07-01
12345 | 2014-08-01
12345 | 2014-09-01
12345 | 2014-10-01
12345 | 2014-11-01
67890 | 2014-03-01
67890 | 2014-04-01
67890 | 2014-05-01
非常感谢您的帮助。
使用每个月的第一天计算每个人的最小和最大日期,然后使用 generate_series
:
在这些日期之间生成基于月的范围
WITH date_ranges AS (
SELECT
person,
min(date_trunc('month', startdate))::timestamptz AS min_start,
max(date_trunc('month', enddate))::timestamptz AS max_end
FROM person_table
GROUP BY 1
)
SELECT
dr.person,
ser.month::DATE as month
FROM date_ranges AS dr,
generate_series(min_start, max_end, '1 month') AS ser(month)
输出
person | month
--------+------------
12345 | 2014-04-01
12345 | 2014-05-01
12345 | 2014-06-01
12345 | 2014-07-01
12345 | 2014-08-01
12345 | 2014-09-01
12345 | 2014-10-01
12345 | 2014-11-01
67890 | 2014-03-01
67890 | 2014-04-01
67890 | 2014-05-01
它是如何工作的?函数调用的隐式 LATERAL JOIN
强制计算输入中的每一行。
此解决方案考虑到每个人可能有超过 1 行的日期,并且它采用可能的最大范围。
无需 CTE 或横向连接:
select
person,
generate_series(
date_trunc('month', startdate),
enddate, '1 month'
)::date as month
from rfem
order by 1, 2
;
person | month
--------+------------
12345 | 2014-04-01
12345 | 2014-05-01
12345 | 2014-06-01
12345 | 2014-07-01
12345 | 2014-08-01
12345 | 2014-09-01
12345 | 2014-10-01
12345 | 2014-11-01
67890 | 2014-03-01
67890 | 2014-04-01
67890 | 2014-05-01
我需要为 table 中每个人的 2 个日期之间的每个月创建一行(结果应该是该月的第一天)。例如,如果我的源 table:
中有以下数据rowID | person | startdate | enddate
1 | 12345 | 2014-04-01 | 2014-11-30
2 | 67890 | 2014-03-01 | 2014-05-01
我希望我的目的地 table 的结果是:
person | month
12345 | 2014-04-01
12345 | 2014-05-01
12345 | 2014-06-01
12345 | 2014-07-01
12345 | 2014-08-01
12345 | 2014-09-01
12345 | 2014-10-01
12345 | 2014-11-01
67890 | 2014-03-01
67890 | 2014-04-01
67890 | 2014-05-01
非常感谢您的帮助。
使用每个月的第一天计算每个人的最小和最大日期,然后使用 generate_series
:
WITH date_ranges AS (
SELECT
person,
min(date_trunc('month', startdate))::timestamptz AS min_start,
max(date_trunc('month', enddate))::timestamptz AS max_end
FROM person_table
GROUP BY 1
)
SELECT
dr.person,
ser.month::DATE as month
FROM date_ranges AS dr,
generate_series(min_start, max_end, '1 month') AS ser(month)
输出
person | month
--------+------------
12345 | 2014-04-01
12345 | 2014-05-01
12345 | 2014-06-01
12345 | 2014-07-01
12345 | 2014-08-01
12345 | 2014-09-01
12345 | 2014-10-01
12345 | 2014-11-01
67890 | 2014-03-01
67890 | 2014-04-01
67890 | 2014-05-01
它是如何工作的?函数调用的隐式 LATERAL JOIN
强制计算输入中的每一行。
此解决方案考虑到每个人可能有超过 1 行的日期,并且它采用可能的最大范围。
无需 CTE 或横向连接:
select
person,
generate_series(
date_trunc('month', startdate),
enddate, '1 month'
)::date as month
from rfem
order by 1, 2
;
person | month
--------+------------
12345 | 2014-04-01
12345 | 2014-05-01
12345 | 2014-06-01
12345 | 2014-07-01
12345 | 2014-08-01
12345 | 2014-09-01
12345 | 2014-10-01
12345 | 2014-11-01
67890 | 2014-03-01
67890 | 2014-04-01
67890 | 2014-05-01