获取表中不同列的最新 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_idtsmeasurement_typevalue。我可以这样做,但是在同一时间戳上发生的不同测量之间存在关系。它们在同一批次中生成,并且数量有限,因此将它们作为列添加到表中是有意义的。顺便说一句,您的解决方案与我的解决方案存在相同的问题:大量重复。 @DandyDev 很多列 - 很多重复。标准化数据 + JSON:dbfiddle.uk/…

以上是关于获取表中不同列的最新 NOT NULL 值,按公共列分组的主要内容,如果未能解决你的问题,请参考以下文章

单个查询从具有不同列的多个表中获取记录

是否有任何其他选项可以从表中获取总计数和同一查询中列的不同计数?

如何从另一个 SQL 表中获取两个不同列的匹配数据:Inner Join 和/或 Union?

复合求和:我想创建一个复合查询,它从两个不同的表中获取两列的单独总和,然后对它们求和

从表中获取最新的两个不同位置

如何从两个不同的、不相关的表中获取最新的行,并将它们合并到一个结果集中?