如何选择集合中的最后一个非NULL列

时间:2014-06-19 04:12:41

标签: sql sqlite

我正在创建一个对每个逻辑行都有一系列修订的表,并使用NULL值来指示特定单元格与上一个修订版本具有相同的值。例如:

id | logical_id | revision | x | y | z | other
---+------------+----------+---+---+---+--------
 1 |     1      |     1    | 1 | 2 | 5 | blue
 2 |     2      |     1    | 3 | 5 | 9 | red
 3 |     1      |     2    | 9 |   |   |
 4 |     2      |     2    |   | 7 |   | orange
 5 |     1      |     3    |   | 6 |   |
 6       3      |     1    | 0 | 0 | 0 | white

我需要一个查询,它接受每个logical_id并构建一行,其中包含修订后每个字段的最新条目("常规字段"),在本例中为x,{{1} },yz。这些字段的数量是任意的,尽管完全可以接受的是,查询需要根据这些字段的实际内容进行修改(我希望它)。

因此对于上表,输出将为:

other

我发现了这个问题," Database: Select last non-null entries",这似乎与我的问题几乎完全相同,但问题是关于Postgresql,并且给出的解决方案是使用语言SQLite中不可用的构造(FIRST_VALUE,PARTITION BY?),似乎只回答部分问题(例如,只查找单个逻辑id的结果),或者当我在SQLite中尝试它们时,它们根本就没有工作

作为额外的奖励,我还希望能够通过设置所有"常规字段"来标记logical_id | x | y | z | other -----------+---+---+---+-------- 1 | 9 | 6 | 5 | blue 2 | 3 | 7 | 9 | orange 3 | 0 | 0 | 0 | white 被删除。如果可能的话,为NULL。

2 个答案:

答案 0 :(得分:1)

要查找每个逻辑ID的最大修订版行的非NULL值,可以使用子查询:

SELECT logical_id,
       (SELECT x
        FROM MyTable
        WHERE logical_id = T.logical_id
          AND x IS NOT NULL
        ORDER BY revision DESC
        LIMIT 1) AS x,
       (SELECT y
        FROM MyTable
        WHERE logical_id = T.logical_id
          AND y IS NOT NULL
        ORDER BY revision DESC
        LIMIT 1) AS y,
       ...
FROM (SELECT DISTINCT logical_id
      FROM MyTable
      -- WHERE ...
     ) AS T

或者,可以使用GROUP BY执行此操作,但是为了在使用MAX搜索最大修订版时能够获取实际列值,需要将修订版和其他值组合成单个字符串,然后提取列值:

SELECT logical_id,
       substr(max(printf('%9d', revision) || x), 10) AS x,
       substr(max(printf('%9d', revision) || y), 10) AS y,
       ...
FROM MyTable
-- WHERE ...
GROUP BY logical_id

要删除最新修订版全为NULL的逻辑ID,请添加如下WHERE子句:

...
WHERE logical_id NOT IN (SELECT logical_id
                         FROM (SELECT logical_id,
                                      max(revision) AS revision
                               FROM MyTable
                               GROUP BY logical_id)
                         JOIN MyTable USING (logical_id, revision)
                         WHERE x     IS NULL
                           AND y     IS NULL
                           AND z     IS NULL
                           AND other IS NULL)
...

答案 1 :(得分:1)

SQLite 3.25引入了窗口函数:

SELECT DISTINCT
  logical_id
 ,FIRST_VALUE(x) OVER(PARTITION BY logical_id ORDER BY CASE WHEN x IS NULL THEN -1
                                                       ELSE id END DESC) AS x
 ,FIRST_VALUE(y) OVER(PARTITION BY logical_id ORDER BY CASE WHEN y IS NULL THEN -1
                                                       ELSE id END DESC) AS y
 ,FIRST_VALUE(z) OVER(PARTITION BY logical_id ORDER BY CASE WHEN z IS NULL THEN -1
                                                       ELSE id END DESC) AS z
 ,FIRST_VALUE(other) OVER(PARTITION BY logical_id ORDER BY CASE WHEN other IS NULL 
                                                THEN -1 ELSE id END DESC) AS other
FROM tab;

db-fiddle.com demo

输出:

+-------------+----+----+----+--------+
| logical_id  | x  | y  | z  | other  |
+-------------+----+----+----+--------+
|          1  | 9  | 6  | 5  | blue   |
|          2  | 3  | 7  | 9  | orange |
|          3  | 0  | 0  | 0  | white  |
+-------------+----+----+----+--------+