如何用WITH子句关联generate_series补全缺失日期为NULL
问题:PostgreSQL查询返回全年所有月份数据(无数据月份返回NULL)
当前查询仅返回有数据的月份(如2021年10-12月),需要调整为返回2021年所有12个月份的数据,无数据的月份对应的sum_estimated和sum_annualiseds字段返回NULL。
原查询语句
WITH dailyhh as ( SELECT distinct on(settlement_period_id) t1.estimated_consumption, t1.settlement_date,t1.annualised_consumption, rank() OVER ( PARTITION BY settlement_period_id ORDER BY CASE settlement_period_interval_count WHEN 0 THEN 1 ELSE 2 END ) FROM consumption_db_schema.halfhourlyconsumption t1 where t1.mprn = '123456789' and t1.settlement_time between '2021-01-01' and '2021-12-31' ) SELECT DATE_TRUNC('month', settlement_date) as settle, sum(estimated_consumption) as sum_estimated, sum(annualised_consumption) as sum_annualiseds FROM dailyhh GROUP BY DATE_TRUNC('month', settlement_date) ORDER BY settle ASC;
预期结果
| settle timestamp with time zone | sum_estimated numeric | sum_annualiseds numeric |
|---|---|---|
| 2021-01-01T00:00:00.000Z | null | null |
| 2021-02-01T00:00:00.000Z | null | null |
| 2021-03-01T00:00:00.000Z | null | null |
| 2021-04-01T00:00:00.000Z | null | null |
| 2021-05-01T00:00:00.000Z | null | null |
| 2021-06-01T00:00:00.000Z | null | null |
| 2021-07-01T00:00:00.000Z | null | null |
| 2021-08-01T00:00:00.000Z | null | null |
| 2021-09-01T00:00:00.000Z | null | null |
| 2021-10-01T00:00:00.000Z | null | 713.2 |
| 2021-11-01T00:00:00.000Z | null | 981 |
| 2021-12-01T00:00:00.000Z | null | 992.3 |
用户尝试的日期序列生成代码
SELECT * FROM ( SELECT day::date FROM generate_series(timestamp '2007-12-01' , timestamp '2008-12-01' , interval '1 month') day ) d
解决方案
核心思路是先生成2021年所有月份的时间序列,再通过左连接关联原查询的聚合结果,确保所有月份被保留,无数据的月份自动填充NULL。
修改后的完整SQL
WITH dailyhh as ( SELECT distinct on(settlement_period_id) t1.estimated_consumption, t1.settlement_date, t1.annualised_consumption FROM consumption_db_schema.halfhourlyconsumption t1 where t1.mprn = '123456789' and t1.settlement_time between '2021-01-01' and '2021-12-31' ORDER BY settlement_period_id, -- 补充distinct on要求的分组内排序规则 CASE settlement_period_interval_count WHEN 0 THEN 1 ELSE 2 END ), monthly_series as ( SELECT generate_series( timestamp '2021-01-01', timestamp '2021-12-01', interval '1 month' ) as settle ) SELECT ms.settle, sum(dh.estimated_consumption) as sum_estimated, sum(dh.annualised_consumption) as sum_annualiseds FROM monthly_series ms LEFT JOIN ( SELECT DATE_TRUNC('month', settlement_date) as settle_month, estimated_consumption, annualised_consumption FROM dailyhh ) dh ON ms.settle = dh.settle_month GROUP BY ms.settle ORDER BY ms.settle ASC;
关键说明
- 生成完整月份序列:用
generate_series生成2021年1月到12月的每个月第一天的时间戳,作为查询的基础维度。 - 左连接关联数据:将原
dailyhh子查询的结果按月份截断后,与生成的月份序列左连接,保证所有月份都被保留。 - 聚合计算:对左连接后的结果按月份分组求和,无数据的月份sum函数会返回NULL。
- 修正distinct on语法:原查询中
distinct on必须配合ORDER BY指定分组内的排序逻辑(即原rank的规则),否则结果可能不稳定,因此补充了对应的排序字段。
内容的提问来源于stack exchange,提问作者constantlyFlagged
相关产品推荐
相关产品推荐

