【问题标题】:How to trade NULL value from a column for last non-NULL value?如何将列中的 NULL 值换成最后一个非 NULL 值?
【发布时间】:2019-07-21 15:05:49
【问题描述】:

我正在实现一个不应该使用 SQLite 和 PostgreSQL 的查询。我的数据库有 2 个表,我想按日期排序,插入传感器的值。我的表如下:

Valor_Sensor_Pressão:

ID_Sensor_Pressão |    Data_Medição    | Valor_Medido
1                  2016-01-01 10:20:05   13
1                  2016-01-01 10:20:06   11
1                  2016-01-01 10:20:12   20
1                  2016-01-01 10:20:13   0
1                  2016-01-01 10:21:05   100

Valor_Sensor_Temperatura:

ID_Sensor_Temperatura |    Data_Medição    | Valor_Medido
1                      2016-01-01 10:20:05   1
1                      2016-01-01 10:20:08   3
1                      2016-01-01 10:20:12   20
1                      2016-01-01 10:21:05   1
1                      2016-01-01 10:21:13   31

我编写了以下 SQL 来按日期聚合和插入数据:

WITH Resultado (ValorPressão, ValorTemperatura, DataMedida) AS (
    SELECT
        p.Valor_Medido AS pValue,
        t.Valor_Medido AS tValue,
        COALESCE(p.Data_Medição, t.Data_Medição) AS DataMedida
    FROM Valor_Sensor_Pressão AS p
        LEFT JOIN Valor_Sensor_Temperatura AS t USING (Data_Medição)
    WHERE p.ID_Sensor_Pressão = 1
    UNION ALL
    SELECT
        p.Valor_Medido AS pValue,
        t.Valor_Medido AS tValue,
        COALESCE(p.Data_Medição, t.Data_Medição) AS DataMedida
    FROM Valor_Sensor_Temperatura AS t
        LEFT JOIN Valor_Sensor_Pressão AS p USING (Data_Medição)
    WHERE
        p.Data_Medição IS NULL
        AND t.ID_Sensor_Temperatura = 1
    ORDER BY COALESCE(p.Data_Medição, t.Data_Medição)
)

SELECT DataMedida, ValorPressão, ValorTemperatura FROM Resultado

结果如下:

"2016-01-01 10:20:05"   "13"    "1"
"2016-01-01 10:20:06"   "11"    NULL
"2016-01-01 10:20:08"   NULL    "3"
"2016-01-01 10:20:12"   "20"    "20"
"2016-01-01 10:20:13"   "0"     NULL
"2016-01-01 10:21:05"   "100"   "1"
"2016-01-01 10:21:13"   NULL    "31"

我已阅读以下page 并针对将在 SQLite 和 PostgreSQL 中使用的查询调整了解决方案:

WITH Resultado (ValorPressão, ValorTemperatura, DataMedida) AS (
    SELECT
        p.Valor_Medido AS pValue,
        t.Valor_Medido AS tValue,
        COALESCE(p.Data_Medição, t.Data_Medição) AS DataMedida
    FROM Valor_Sensor_Pressão AS p
        left join Valor_Sensor_Temperatura t USING (Data_Medição)
    WHERE p.ID_Sensor_Pressão = 1
    UNION ALL
    SELECT
        p.Valor_Medido AS pValue,
        t.Valor_Medido AS tValue,
        COALESCE(p.Data_Medição, t.Data_Medição) AS DataMedida
    FROM Valor_Sensor_Temperatura AS t
        LEFT JOIN Valor_Sensor_Pressão AS p USING (Data_Medição)
    WHERE
        p.Data_Medição IS NULL
        AND t.ID_Sensor_Temperatura = 1
    ORDER BY COALESCE(p.Data_Medição, t.Data_Medição)
)

SELECT
    DataMedida,
    COALESCE(last_value(ValorPressão) OVER (order by DataMedida), 0) AS ValorPressão,
    COALESCE(last_value(ValorTemperatura) over (order by DataMedida), 0) AS ValorTemperatura
FROM Resultado

我无法弄清楚错误是什么,因为 LAST_VALUE 没有给我上一行的值,所以 COALESCE 用 0 代替了之前的值,我找不到 SQLite 的 IGNORE NULLS 的替代品。

哪些修改会返回列的先前值而不是 NULL?给我这样的东西:

"2016-01-01 10:20:05"   "13"    "1"
"2016-01-01 10:20:06"   "11"    "1"
"2016-01-01 10:20:08"   "11"    "3"
"2016-01-01 10:20:12"   "20"    "20"
"2016-01-01 10:20:13"   "0"     "20"
"2016-01-01 10:21:05"   "100"   "1"
"2016-01-01 10:21:13"   "100    "31"

编辑:我将从其他表中获得的 ID,因此我将 WHERE 选择为“1”只是为了测试。

【问题讨论】:

    标签: sql postgresql sqlite


    【解决方案1】:

    我已经找出问题所在,我在 LAG 和 LAST_VALUE 上犯了一个错误。解决方案是将两者合并到最后一个 COALESCE 中并添加 FIRST_VALUE。

    我在“Valor_Sensor_Pressão”中添加了一个新行,以检查第一行何时为 NULL:

    "1" "2016-01-01 10:20:00"   "5.5"
    

    所以我的最终查询是:

    WITH Resultado (ValorPressão, ValorTemperatura, DataMedida) AS (
        SELECT
            p.Valor_Medido AS pValue,
            t.Valor_Medido AS tValue,
            COALESCE(p.Data_Medição, t.Data_Medição) AS DataMedida
        FROM Valor_Sensor_Pressão AS p
            LEFT JOIN Valor_Sensor_Temperatura t USING (Data_Medição)
        WHERE p.ID_Sensor_Pressão = 1
        UNION ALL
        SELECT
            p.Valor_Medido AS pValue,
            t.Valor_Medido AS tValue,
            COALESCE(p.Data_Medição, t.Data_Medição) AS DataMedida
        FROM Valor_Sensor_Temperatura AS t
            LEFT JOIN Valor_Sensor_Pressão AS p USING (Data_Medição)
        WHERE
            p.Data_Medição IS NULL
            AND t.ID_Sensor_Temperatura = 1
        ORDER BY COALESCE(p.Data_Medição, t.Data_Medição)
    )
    
    SELECT
        DataMedida,
        COALESCE(LAST_VALUE(ValorPressão) OVER (ORDER BY DataMedida),LAG(ValorPressão) OVER (ORDER BY DataMedida), FIRST_VALUE(ValorPressão) OVER (ORDER BY DataMedida),0) AS ValorPressão,
        COALESCE(LAST_VALUE(ValorTemperatura) OVER (ORDER BY DataMedida), LAG(ValorTemperatura) OVER (ORDER BY DataMedida), FIRST_VALUE(ValorTemperatura) OVER (ORDER BY DataMedida),0) AS ValorTemperatura
    FROM Resultado
    

    我的结果是:

    "2016-01-01 10:20:00"   "5.5"   "0"
    "2016-01-01 10:20:05"   "13"    "1"
    "2016-01-01 10:20:06"   "11"    "1"
    "2016-01-01 10:20:08"   "11"    "3"
    "2016-01-01 10:20:12"   "20"    "20"
    "2016-01-01 10:20:13"   "0"     "20"
    "2016-01-01 10:21:05"   "100"   "1"
    "2016-01-01 10:21:13"   "100"   "31"
    

    【讨论】:

      【解决方案2】:

      您可以更轻松地实现当前查询:

      select id, data, Valor_Medido, valor_temperatura
      from (select ID_Sensor_Pressã as id, Data_Medição as data, Valor_Medido, null as valor_temperatura
            from valor_Sensor_Pressão
            union all
            select ID_Sensor_Temperatura, Data_Medição, null, Valor_Medido
            from Valor_Sensor_Temperatura
           ) v
      group by id, data;
      

      然后,您想要做的事情在 Postgres 中使用窗口函数更容易完成。但对于一般解决方案,您可以使用子查询:

      with v as (
            select id, data, max(Valor_Medido) as Valor_Medido, max(valor_temperatura) as valor_temperatura
            from (select ID_Sensor_Pressã as id, Data_Medição as data, Valor_Medido, null as valor_temperatura
                  from valor_Sensor_Pressão
                  union all
                  select ID_Sensor_Temperatura, Data_Medição, null, Valor_Medido
                  from Valor_Sensor_Temperatura
                 ) v
            group by id, data
           )
      select v.*,
             coalesce(Valor_Medido,
                      (select v2.Valor_Medido
                       from v v2
                       where v2.id = v.id and v2.data < v.data
                       order by v2.data desc
                       limit 1
                      )
                     ) as valor_medido,
             coalesce(valor_temperatura,
                      (select v2.valor_temperatura
                       from v v2
                       where v2.id = v.id and v2.data < v.data
                       order by v2.data desc
                       limit 1
                      )
                     ) as valor_temperatura,
      from v;
      

      【讨论】:

      • 您好,感谢您的回答!我删除了 ID 部分,因为它将来自其他表。但是,当我尝试第一个查询时,结果与我首先想要的不同。此外,出现一个错误,说最后一个“FROM”之后有语法错误,这是没有意义的,因为您定义了“v”。我会继续尝试遵循您的建议。
      猜你喜欢
      • 2017-06-24
      • 1970-01-01
      • 2021-10-17
      • 1970-01-01
      • 1970-01-01
      • 2014-08-09
      • 2011-10-26
      • 2020-06-21
      • 1970-01-01
      相关资源
      最近更新 更多