获取表中不同列的最新 NOT NULL 值,按公共列分组
Posted
技术标签:
【中文标题】获取表中不同列的最新 NOT NULL 值,按公共列分组【英文标题】:Obtain latest NOT NULL values for different columns in a table, grouped by common column 【发布时间】:2021-09-28 17:47:03 【问题描述】:在 PostgreSQL 数据库中,我有一个如下所示的测量表:
| sensor_group_id | ts | value_1 | value_2 | etc... |
|-----------------|---------------------------|---------|---------|--------|
| 1 | 2021-07-21T00:20:00+00:00 | 15 | NULL | |
| 1 | 2021-07-15T00:20:00+00:00 | NULL | 23 | |
| 2 | 2021-07-17T00:20:00+00:00 | NULL | 11 | |
| 1 | 2021-07-13T00:20:00+00:00 | 9 | 4 | |
| 2 | 2021-07-10T00:20:00+00:00 | 99 | 36 | |
此表中有许多列具有不同类型的测量值。每个传感器组同时产生不同类型的测量值,但并不总是所有类型。 所以我们最终得到了部分填充的行。
我想做什么:
对于每个不同的 sensor_group_id 针对每个不同的列(测量类型) 获取该列不为 NULL 时的最新时间戳以及该时间戳处该度量的值我现在的解决方案,看起来很麻烦:
WITH
latest_value_1 AS (SELECT DISTINCT ON (sensor_group_id) sensor_group_id, ts, value_1
FROM measurements
WHERE value_1 IS NOT NULL
ORDER BY sensor_group_id, ts DESC),
latest_value_2 AS (SELECT DISTINCT ON (sensor_group_id) sensor_group_id, ts, value_2
FROM measurements
WHERE value_2 IS NOT NULL
ORDER BY sensor_group_id, ts DESC),
latest_value_3 AS (SELECT DISTINCT ON (sensor_group_id) sensor_group_id, ts, value_3
FROM measurements
WHERE value_3 IS NOT NULL
ORDER BY sensor_group_id, ts DESC),
etc...
SELECT latest_value_1.sensor_group_id,
latest_value_1.ts AS latest_value_1_ts,
value_1,
latest_value_2.ts AS latest_value_2_ts,
value_2,
latest_value_3.ts AS latest_value_3_ts,
value_3,
etc...
FROM lastest_value_1
JOIN latest_value_2
ON latest_value_1.sensor_group_id = latest_value_2.sensor_group_id
JOIN latest_value_2
ON latest_value_1.sensor_group_id = latest_value_2.sensor_group_id
JOIN latest_value_3
ON latest_value_1.sensor_group_id = latest_value_3.sensor_group_id
etc...
这会产生以下结果:
sensor_group_id | latest_value_1_ts | value_1 | latest_value_2_ts | value_2 | etc... |
---|---|---|---|---|---|
1 | 2021-07-21T00:20:00+00:00 | 15 | 2021-07-21T00:20:00+00:00 | 23 | |
2 | 2021-07-10T00:20:00+00:00 | 99 | 2021-07-17T00:20:00+00:00 | 11 |
这看起来异常复杂,但我不确定是否有更好的方法。非常感谢您的帮助!
【问题讨论】:
【参考方案1】:您真正想要的是LAG()
或LAST_VALUE()
上的IGNORE NULLS
选项。但是 Postgres 不支持这个功能。相反,您可以使用两级技巧,为每个值分配一个分组,因此每个 NULL
值与具有值的前一行位于同一组中。然后通过组“schmear”值:
select t.*,
max(value_1) over (partition by sensor_group_id, grp_1) as imputed_value_1,
max(value_2) over (partition by sensor_group_id, grp_2) as imputed_value_2,
max(value_3) over (partition by sensor_group_id, grp_3) as imputed_value_3
from (select t.*,
count(value_1) over (partition by sensor_group_id order by ts) as grp_1,
count(value_2) over (partition by sensor_group_id order by ts) as grp_2,
count(value_3) over (partition by sensor_group_id order by ts) as grp_3
from t
) t;
【讨论】:
【参考方案2】:不确定是不是更简单...
with
sensor_groups(sgr_id) as ( -- Change it to the list of groups if you have it
select distinct sensor_group_id from measurements)
select
*
from
sensor_groups as sg
left join lateral (
select ts, value_1
from measurements
where value_1 is not null and sensor_group_id = sg.sgr_id
order by ts desc limit 1) as v1(ts_1, v_1) on true
left join lateral (
select ts, value_2
from measurements
where value_2 is not null and sensor_group_id = sg.sgr_id
order by ts desc limit 1) as v2(ts_2, v_2) on true
...
PS:数据规范化会很有帮助
【讨论】:
我假设规范化是指只有 4 列的表:sensor_group_id
、ts
、measurement_type
和 value
。我可以这样做,但是在同一时间戳上发生的不同测量之间存在关系。它们在同一批次中生成,并且数量有限,因此将它们作为列添加到表中是有意义的。顺便说一句,您的解决方案与我的解决方案存在相同的问题:大量重复。
@DandyDev 很多列 - 很多重复。标准化数据 + JSON:dbfiddle.uk/…以上是关于获取表中不同列的最新 NOT NULL 值,按公共列分组的主要内容,如果未能解决你的问题,请参考以下文章
是否有任何其他选项可以从表中获取总计数和同一查询中列的不同计数?
如何从另一个 SQL 表中获取两个不同列的匹配数据:Inner Join 和/或 Union?