【问题标题】:SQL fill null values for a field in multiple rows as previous non null valueSQL将多行中的字段的空值填充为先前的非空值
【发布时间】:2016-11-26 08:14:58
【问题描述】:

我在 netezza 中有一个表(基于 postgresql),如下所示。我需要创建一个新表,其中 name 的 NULL 值替换为前一个非空行的 name 值。

table1

id name time    value
---------------------
1 john  11:00   324
2 NULL  12:00   645
3 NULL  13:00   324
4 bane  11:00   132
5 NULL  12:00   30
6 NULL  13:00   NULL
7 NULL  14:00   -1
8 zane  11:00   152
9 NULL  12:00   60
10 NULL 13:00   NULL

输出表

name    time    value
---------------------
john    11:00   324
john    12:00   645
john    13:00   324
bane    11:00   132
bane    12:00   30
bane    13:00   NULL
bane    14:00   -1
zane    11:00   152
zane    12:00   60
zane    13:00   NULL
笔记:
  1. 由于权限限制,无法更改 table1,因此创建一个新表 是方式。

  2. 需要在Netezza(最好)或MS Access中运行。

用于在 Netezza 中创建测试数据的代码如下。

create temp table test (
    id int
    ,name varchar(10)
    ,time time
    ,value int
)distribute on random;

insert into test (id, name, time, value) values(1, 'joe', '10:00', 324);
insert into test (id, name, time, value) values(2, null, '11:00', 645);
insert into test (id, name, time, value) values(3, null, '12:00', 324);

insert into test (id, name, time, value) values(4, 'bane', '10:00', 132);
insert into test (id, name, time, value) values(5, null, '11:00', 30);
insert into test (id, name, time, value) values(6, null, '12:00', null);
insert into test (id, name, time, value) values(7, null, '13:00', -1);

insert into test (id, name, time, value) values(8, 'zane', '10:00', 152);
insert into test (id, name, time, value) values(9, null, '11:00', 60);
insert into test (id, name, time, value) values(10, null, '12:00', null);

【问题讨论】:

    标签: sql postgresql ms-access netezza


    【解决方案1】:

    试试这个递归 PostgreSQL 查询:

    WITH RECURSIVE t(id, name, time, value) AS (
      SELECT id, name, time, value FROM test WHERE id = (
        SELECT MIN(id) FROM test
      )
      UNION
      SELECT test.id, COALESCE(test.name, t.name), test.time, test.value
      FROM test, t WHERE test.id = (
        SELECT id FROM test WHERE id > t.id ORDER BY id LIMIT 1
      )
    ) SELECT * FROM t ORDER BY id;
    

    请注意,这可能会在每一行上发出SELECT。如果你不想这样,你可以使用视图解决方案:

    CREATE VIEW test_view AS
    SELECT id, LAG(id) OVER (ORDER BY id) lag_id, name, time, value FROM test;
    
    WITH RECURSIVE t(id, name, time, value) AS (
      SELECT id, name, time, value FROM test_view WHERE lag_id IS NULL
      UNION ALL
      SELECT test_view.id, COALESCE(test_view.name, t.name),
        test_view.time, test_view.value
      FROM test_view, t WHERE test_view.lag_id = t.id
    ) SELECT * FROM t ORDER BY id;
    

    这应该快得多。这个想法来自this article。 SQLFiddle:http://sqlfiddle.com/#!15/63f7b/1/1.

    【讨论】:

    • 这应该可以在 Postgresql 中使用。但是我需要在 Netezza(基于 Postgresql)上运行它并在运行它时收到以下错误 - ERROR: Recursive queries are not supported。所以在这种情况下似乎不能使用递归。
    【解决方案2】:

    这在 Access 2010 中适用于我:

    SELECT 
        t1.id,
        (
            SELECT TOP 1 t2.name
            FROM test t2
            WHERE t2.id<=t1.id AND t2.name IS NOT NULL
            ORDER BY t2.id DESC
        ) AS name,
        t1.time,
        t1.value
    FROM test t1
    

    它也应该适用于其他 SQL 方言,尽管它们的处理方式可能略有不同 TOP 1(例如,LIMIT 1 是一个常见的变体)。

    【讨论】:

      【解决方案3】:

      您可以使用COALESCE 函数和子查询来实现这一点:

      SELECT t.id, 
          COALESCE(t.name, (SELECT s.name FROM table s WHERE s.name IS NOT NULL AND s.id < t.id ORDER BY s.id LIMIT 1)) AS name,
          t.time,
          COALESCE(t.value, (SELECT s.value FROM table s WHERE s.value IS NOT NULL AND s.id < t.id ORDER BY s.id LIMIT 1)) AS value
      FROM table t ORDER BY t.id
      

      【讨论】:

      • 这不起作用。错误:"SELECT" (at char 31) expecting an identifier found a keyword
      • 哦,查询需要更多的括号。我会解决的。
      • 错误:ERROR: (2) This form of correlated query is not supported - 考虑重写。另请注意,我在Netezza 中运行它,它基于 PostgreSQL
      • 这可能来自子查询中的多个结果行。我添加了排序和限制。
      • 试过这个(在 netezza 上) - 但得到同样的错误 - ERROR: (2) This form of correlated query is not supported - consider rewriting
      【解决方案4】:

      基于 MS Access 的解决方案

      SELECT (SELECT last(name)
                FROM test AS temp
                WHERE test.id >= temp.id AND temp.name IS NOT NULL) AS new_name, *
      FROM test;
      

      【讨论】:

        【解决方案5】:

        尝试使用引导功能。不确定这是否适用于 postgre,对于 Oracle 则适用。我认为这会有所帮助。

        【讨论】:

        • 如何计算LEAD 所需的偏移量?
        • 好问题。没想到。
        猜你喜欢
        • 2021-09-30
        • 1970-01-01
        • 2016-06-28
        • 1970-01-01
        • 1970-01-01
        • 2013-05-01
        • 1970-01-01
        • 2013-02-10
        • 2014-04-06
        相关资源
        最近更新 更多