【问题标题】:How to select last non-NULL columns in a set如何选择集合中的最后一个非 NULL 列
【发布时间】:2014-08-09 12:32:18
【问题描述】:

我正在创建一个表,该表对每个逻辑行都有一系列修订,并使用 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 并为修订后的每个字段(“常规字段”)构建一个包含最新条目的行,在本例中为xyz 和@987654326 @。此类字段的数量是任意的,尽管需要根据这些字段的实际情况来修改查询是完全可以接受的(我希望如此)。

所以对于上表,输出将是:

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

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

另外,如果可能的话,我还希望能够通过将所有“常规字段”设置为 NULL 来将 logical_id 标记为已删除。

【问题讨论】:

  • 输出真的应该有idrevision 列吗?他们的价值观应该是什么?
  • @CL。你是对的,输出中不需要这些列。我已经更新了问题。

标签: sql sqlite


【解决方案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)
...

【讨论】:

  • 当然可以,但它... 2000 行超过 6 秒。有了这种性能,似乎最好在每个版本中接受冗余数据。
  • 第一个查询可以通过logical_idrevision 上的两列索引来加速。
  • 98ms,这是一个相当大的进步。再次感谢!
【解决方案2】:

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  |
+-------------+----+----+----+--------+

【讨论】:

    猜你喜欢
    • 2019-07-21
    • 1970-01-01
    • 2014-09-05
    • 2021-12-23
    • 2014-11-11
    • 1970-01-01
    • 2014-04-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多