【问题标题】:Select a row of first non-null values in a sparse table在稀疏表中选择一行第一个非空值
【发布时间】:2014-01-11 09:51:45
【问题描述】:

使用下表:

A | B    | C    | ts
--+------+------+------------------
1 | null | null | 2016-06-15 10:00
4 | null | null | 2016-06-15 11:00 
4 |    9 | null | 2016-06-15 12:00
5 |    1 |    7 | 2016-06-15 13:00

如何在 N 行的运行窗口中选择每列的第一个非空值? “第一”由ts 列中的时间戳顺序定义。查询上表会得到:

A | B | C
--+---+---
1 | 9 | 7

【问题讨论】:

    标签: sql postgresql null window-functions


    【解决方案1】:

    您应该定义一个顺序(主键或其他内容)以获取 FIRST 非空值。所以我使用ID 列对表中的行进行排序。

    select 
    (select A from t where A is not null ORDER BY id LIMIT 1),
    (select b from t where b is not null ORDER BY id LIMIT 1),
    (select c from t where c is not null ORDER BY id LIMIT 1)
    

    SQLFiddle demo

    【讨论】:

      【解决方案2】:

      窗口函数first_value() 提供了一个相当简短而优雅的解决方案:

      SELECT first_value(a) OVER (ORDER BY a IS NULL, ts) AS a
           , first_value(b) OVER (ORDER BY b IS NULL, ts) AS b
           , first_value(c) OVER (ORDER BY c IS NULL, ts) AS c
      FROM   t
      LIMIT  1;
      

      a IS NULL 的计算结果为 TRUEFALSEFALSE 排在 TRUE 之前。这样,非空值首先出现。接下来按ts(如您评论的时间戳列)订购,您将在一个SELECT 中得到它。

      如果 Postgres 支持 IGNORE NULLS,这会更简单。 The manual:

      SQL 标准定义了 RESPECT NULLSIGNORE NULLS 选项 leadlagfirst_valuelast_valuenth_value。这不是 在 PostgreSQL 中实现:行为总是与 标准的默认值,即RESPECT NULLS

      这是该领域标准 SQL 的少数遗漏之一。

      db小提琴here
      SQL Fiddle.

      【讨论】:

      • ORDER BY a IS NULLorder by a nulls first 相比有什么优势吗?
      • @a_horse_with_no_name: NULLS LAST 在这种情况下。但两者都不等价。 ORDER BY a IS NULL 仅对 NULL 值进行最后排序,其余的保持 未排序 - 在下一次迭代中由 ts 排序。 ORDER BY a NULLS LAST 做的太多了。
      【解决方案3】:

      您可以使用window functions 执行此操作。我已将结果分成两部分,然后将此分区用于row_number window function

      1. 具有空值
      2. 有一个有效的有效

      然后,使用基本的case 来获取具有row_number1 并且其中具有not null 值的那些

      SQLFIDDLE

      SELECT
          max ( CASE
                  WHEN a_row_num = 1 AND a IS NOT NULL THEN a
                END ) AS A,
          max ( CASE
                  WHEN b_row_num = 1 AND B IS NOT NULL THEN B
                END ) AS B,
          max ( CASE
                  WHEN c_row_num = 1 AND C IS NOT NULL THEN C
                END ) AS C
      FROM
          (
            SELECT
                a,
                row_number ( ) over ( partition BY a IS NULL ORDER BY ID ) a_row_num,
                b,
                row_number ( ) over ( partition BY b IS NULL ORDER BY ID ) b_row_num,
                c,
                row_number ( ) over ( partition BY c IS NULL ORDER BY ID ) c_row_num
            FROM
                test
          ) AS sub_query
      

      输出:

      | A | B | C |
      |---|---|---|
      | 1 | 9 | 7 |
      

      注意:我添加了一个id 字段,它有助于了解首先插入了哪些记录,我们在窗口函数中按升序使用它

      【讨论】:

        【解决方案4】:

        不确定我的问题是否正确
        因为它基本上看起来很简单。
        试试这个查询。

        SQL 小提琴:http://sqlfiddle.com/#!11/ac585/8

        WITH t0 AS
        (
            SELECT A FROM
            TableName t0
            WHERE (A IS NOT NULL)
            ORDER BY ID ASC
            LIMIT 1
        ),
        
        t1 AS
        (
            SELECT B FROM
            TableName
            WHERE (B IS NOT NULL)
            ORDER BY ID ASC
            LIMIT 1
        ),
        
        t2 AS
        
        (
            SELECT C FROM
            TableName
            WHERE (C IS NOT NULL)
            ORDER BY ID ASC
            LIMIT 1
        )
        
        SELECT t0.A, t1.B, t2.C
        FROM
        t0
        JOIN t1 ON 1=1
        JOIN t2 ON 1=1
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2019-07-01
          • 2018-09-26
          • 1970-01-01
          • 2015-08-25
          • 2021-05-27
          • 1970-01-01
          • 2019-10-12
          相关资源
          最近更新 更多