PostgreSQL外连接补全空白:如何填充最新已知参数值?
外连接“补全空白”
在PostgreSQL数据库中有一对主从表:
- 主表
samples:存储带时间戳的样本记录 - 从表
sample_values:存储对应样本时间戳下部分参数的数值
当前查询
SELECT s.sample_id, s.sample_time, v.parameter_id, v.sample_value FROM samples s LEFT OUTER JOIN sample_values v ON v.sample_id=s.sample_id ORDER BY s.sample_id, v.parameter_id;
返回结果(符合预期)
| sample_id | sample_time | parameter_id | sample_value |
|---|---|---|---|
| 1 | 2023-01-13T01:00:00.000Z | 1 | 1.23 |
| 1 | 2023-01-13T01:00:00.000Z | 2 | 4.98 |
| 2 | 2023-01-13T01:01:00.000Z | ||
| 3 | 2023-01-13T01:02:00.000Z | ||
| 4 | 2023-01-13T01:03:00.000Z | ||
| 5 | 2023-01-13T01:04:00.000Z | 2 | 6.08 |
| 6 | 2023-01-13T01:05:00.000Z | ||
| 7 | 2023-01-13T01:06:00.000Z | 1 | 1.89 |
| 8 | 2023-01-13T01:07:00.000Z | ||
| 9 | 2023-01-13T01:08:00.000Z | ||
| 10 | 2023-01-13T01:09:00.000Z | ||
| 11 | 2023-01-13T01:10:00.000Z | ||
| 12 | 2023-01-13T01:11:00.000Z | ||
| 13 | 2023-01-13T01:12:00.000Z | ||
| 14 | 2023-01-13T01:13:00.000Z | ||
| 15 | 2023-01-13T01:14:00.000Z | 1 | 2.11 |
| 16 | 2023-01-13T01:15:00.000Z | ||
| 17 | 2023-01-13T01:16:00.000Z | ||
| 18 | 2023-01-13T01:17:00.000Z | ||
| 19 | 2023-01-13T01:18:00.000Z | 2 | 3.57 |
| 20 | 2023-01-13T01:19:00.000Z | ||
| 21 | 2023-01-13T01:20:00.000Z | ||
| 22 | 2023-01-13T01:21:00.000Z | ||
| 23 | 2023-01-13T01:22:00.000Z | 1 | 3.21 |
| 23 | 2023-01-13T01:22:00.000Z | 2 | 5.31 |
需求
如何编写查询语句,使结果中每个时间戳对应每个参数一行,且sample_value为该参数的最新已知值?示例结果如下:
| sample_id | sample_time | parameter_id | sample_value |
|---|---|---|---|
| 1 | 2023-01-13T01:00:00.000Z | 1 | 1.23 |
| 1 | 2023-01-13T01:00:00.000Z | 2 | 4.98 |
| 2 | 2023-01-13T01:01:00.000Z | 1 | 1.23 |
| 2 | 2023-01-13T01:01:00.000Z | 2 | 4.98 |
| 3 | 2023-01-13T01:02:00.000Z | 1 | 1.23 |
| 3 | 2023-01-13T01:02:00.000Z | 2 | 4.98 |
| 4 | 2023-01-13T01:03:00.000Z | 1 | 1.23 |
| 4 | 2023-01-13T01:03:00.000Z | 2 | 4.98 |
| 5 | 2023-01-13T01:04:00.000Z | 1 | 1.23 |
| 5 | 2023-01-13T01:04:00.000Z | 2 | 6.08 |
| 6 | 2023-01-13T01:05:00.000Z | 1 | 1.23 |
| 6 | 2023-01-13T01:05:00.000Z | 2 | 6.08 |
| 7 | 2023-01-13T01:06:00.000Z | 1 | 1.89 |
| 7 | 2023-01-13T01:06:00.000Z | 2 | 6.08 |
| 8 | 2023-01-13T01:07:00.000Z | 1 | 1.89 |
| 8 | 2023-01-13T01:07:00.000Z | 2 | 6.08 |
疑问与函数说明
我不确定LAST_VALUE函数是否适用于此场景,它的语法及中文翻译如下:
原语法
LAST_VALUE ( expression ) OVER ( [PARTITION BY partition_expression, ... ] ORDER BY sort_expression [ASC | DESC], ... )
中文翻译
LAST_VALUE(表达式) OVER ( [PARTITION BY 分区表达式, ... ] ORDER BY 排序表达式 [ASC | DESC], ... )
解决方案
LAST_VALUE完全适用于这个场景,结合笛卡尔积生成全量参数-样本对,再配合窗口函数即可实现需求:
方法一(PostgreSQL 11+,支持IGNORE NULLS)
WITH all_parameters AS ( -- 获取所有唯一参数ID SELECT DISTINCT parameter_id FROM sample_values ), sample_parameter_pairs AS ( -- 生成每个样本与每个参数的组合 SELECT s.sample_id, s.sample_time, p.parameter_id FROM samples s CROSS JOIN all_parameters p ), filled_values AS ( SELECT spp.sample_id, spp.sample_time, spp.parameter_id, -- 按参数分区、时间排序,取当前行及之前最新的非空值 LAST_VALUE(v.sample_value) OVER ( PARTITION BY spp.parameter_id ORDER BY spp.sample_time ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW IGNORE NULLS ) AS sample_value FROM sample_parameter_pairs spp LEFT JOIN sample_values v ON spp.sample_id = v.sample_id AND spp.parameter_id = v.parameter_id ) SELECT * FROM filled_values ORDER BY sample_id, parameter_id;
方法二(兼容低版本PostgreSQL)
如果你的PostgreSQL版本低于11,不支持IGNORE NULLS,可以用分组累加的方式实现:
WITH all_parameters AS ( SELECT DISTINCT parameter_id FROM sample_values ), sample_parameter_pairs AS ( SELECT s.sample_id, s.sample_time, p.parameter_id FROM samples s CROSS JOIN all_parameters p ), grouped_values AS ( SELECT spp.*, v.sample_value, -- 每次遇到非空值时生成新分组 SUM(CASE WHEN v.sample_value IS NOT NULL THEN 1 ELSE 0 END) OVER ( PARTITION BY spp.parameter_id ORDER BY spp.sample_time ) AS value_group FROM sample_parameter_pairs spp LEFT JOIN sample_values v ON spp.sample_id = v.sample_id AND spp.parameter_id = v.parameter_id ), filled_values AS ( SELECT sample_id, sample_time, parameter_id, -- 同分组内取最新的非空值 MAX(sample_value) OVER ( PARTITION BY parameter_id, value_group ORDER BY sample_time ) AS sample_value FROM grouped_values ) SELECT * FROM filled_values ORDER BY sample_id, parameter_id;
内容的提问来源于stack exchange,提问作者TheRoadrunner
相关产品推荐
相关产品推荐

