PostgreSQL 9.4.26中如何生成日期序列并保留首尾日期
问题描述
使用PostgreSQL 9.4.26,现有表格数据如下:
| Key | Start | End |
|---|---|---|
| ABC123 | 24/01/2012 | 23/01/2013 |
需要将该跨年度的日期范围按月拆分,得到每个月对应的日期区间:第一个月保留原起始日期(24/01/2012),最后一个月保留原结束日期(23/01/2013),中间月份为完整自然月。
原查询生成的第一条记录起始日期为1/01/2012,不符合预期,需修正。
原查询问题分析
原查询中generate_series的起始值使用date_trunc('month', start_date),直接取原起始日期所在月份的第一天,导致第一个月的起始日期丢失了原数据的具体日份。
修正后的查询语句
WITH date_ranges AS ( SELECT "Key", TO_DATE("Start", 'DD/MM/YYYY') AS start_date, TO_DATE("End", 'DD/MM/YYYY') AS end_date FROM tabl ) SELECT "Key", TO_CHAR( CASE WHEN month_start = date_trunc('month', start_date) THEN start_date ELSE month_start END, 'DD/MM/YYYY' ) AS "Start", TO_CHAR( LEAST( (month_start + INTERVAL '1 month') - INTERVAL '1 day', end_date ), 'DD/MM/YYYY' ) AS "End" FROM date_ranges CROSS JOIN LATERAL generate_series( date_trunc('month', start_date), date_trunc('month', end_date), INTERVAL '1 month' ) AS month_start(month_start);
关键修改说明
- 保留原Key字段:在CTE
date_ranges中加入"Key",确保输出结果包含该字段。 - 修正起始日期逻辑:通过
CASE判断生成的月份起始是否为原起始日期所在月份的月初,若是则使用原start_date,否则使用当月第一天,保证第一个月保留原起始日。 - 调整序列结束值:将
generate_series的结束值改为date_trunc('month', end_date),确保序列覆盖到结束日期所在月份的月初,再通过LEAST函数处理最后一个月的结束日期为原end_date。
验证结果
执行修正后的语句,将得到预期的拆分结果:
| Key | Start | End |
|---|---|---|
| ABC123 | 24/01/2012 | 31/01/2012 |
| ABC123 | 1/02/2012 | 29/02/2012 |
| ABC123 | 1/03/2012 | 31/03/2012 |
| ABC123 | 1/04/2012 | 30/04/2012 |
| ABC123 | 1/05/2012 | 31/05/2012 |
| ABC123 | 1/06/2012 | 30/06/2012 |
| ABC123 | 1/07/2012 | 31/07/2012 |
| ABC123 | 1/08/2012 | 31/08/2012 |
| ABC123 | 1/09/2012 | 30/09/2012 |
| ABC123 | 1/10/2012 | 31/10/2012 |
| ABC123 | 1/11/2012 | 30/11/2012 |
| ABC123 | 1/12/2012 | 31/12/2012 |
| ABC123 | 1/01/2013 | 23/01/2013 |
内容的提问来源于stack exchange,提问作者vin.1056465
相关产品推荐
相关产品推荐

