【问题标题】:Fill in NULL column values with lag(N) values用 lag(N) 值填充 NULL 列值
【发布时间】:2014-05-28 20:15:32
【问题描述】:

我有一个简单的表,其中缺少“某人”列的值。我想用 id 字段中的先前值按升序填充 NULL 值,而不是降序值(过去的项目可能不同)。为了实验(我的实际查询要复杂得多),我不能简单地使用 UPDATE 查询来填充表,我必须将其作为 SELECT 来执行。

CREATE TABLE lag_test (id serial primary key, natural_key integer, somebody text);

INSERT INTO lag_test(natural_key, somebody)
VALUES (1, NULL), (1, 'Kirk'), (1, NULL), (2, 'Roybal'), (2, NULL), (2, NULL);

示例代码创建如下表格:

id  natural_key  somebody
--  -----------  --------
1   1            NULL
2   1            Kirk
3   1            NULL
4   2            Roybal
5   2            NULL
6   2            NULL

到目前为止,我有这个:

SELECT id,
       natural_key,
       COALESCE(somebody, lag(somebody) OVER (PARTITION BY natural_key)) somebody
FROM lag_test
ORDER BY natural_key, id;

返回这个:

id  natural_key  somebody
--  -----------  --------
1   1            NULL
2   1            Kirk
3   1            Kirk
4   2            Roybal
5   2            Roybal
6   2            NULL

我希望它返回这个:

id  natural_key  somebody
--  -----------  --------
1   1            NULL
2   1            Kirk
3   1            Kirk
4   2            Roybal
5   2            Roybal
6   2            Roybal

基本问题是:如何让 lag() 过去 N 行,以便行 id:6,natural_key:2 接收“某人”列的值?

我正在使用 PG 9.3.4。

更新: 阅读文档后,我发现 lag 需要一个可选参数 [offset],我可以在某种程度上使用它。希望有人能帮我完善一下:

SELECT id,
       natural_key,
       COALESCE(somebody,
                lag(somebody, 1) OVER (PARTITION BY natural_key),
                lag(somebody, 2) OVER (PARTITION BY natural_key),
                lag(somebody, 3) OVER (PARTITION BY natural_key)
               ) somebody
FROM lag_test
ORDER BY natural_key, id;

这解决了 OP 中显示的有限测试集的问题。真正的问题尚未得到解答。

编辑 2:

我也发现了这个小宝石。

SELECT id, natural_key, 
  regexp_replace(string_agg(somebody, '|') OVER (ORDER BY id)::text, '^.*\|', '', 'g') somebody 
FROM lag_test 
ORDER BY natural_key, id;

仅适用于不包含管道“|”的数据象征。有点hacky,但性能很好。

【问题讨论】:

  • 在匿名代码块中进行足够的更新很容易。你会接受这样的灵魂吗?
  • 我不确定您所说的“充分更新”是什么意思,抱歉。
  • 你想做的更新。
  • 我不能使用 UPDATE 语句。例子很简单,但实际用例却很复杂。我将不胜感激与窗口功能相关的答案,而不是表更新。
  • 对不起,我错过了。我担心 lag() 的解决方案会非常复杂。用户定义的功能仍然很简单。

标签: sql postgresql postgresql-9.3


【解决方案1】:

根据您显示的内容,这是具有正确输出的一个。为了扩展测试,我输入了更多值,创建了两个不同的名称,它们之间有一个间隔。

CREATE TABLE lag_test(
  id serial primary key,
  natural_key integer,
  somebody text);

INSERT  INTO lag_test( natural_key, somebody )
VALUES  (1, NULL), (1, 'Kirk'), (1, NULL), (1, NULL), (1, 'James'), (1, NULL),
        (2, 'Roybal'), (2, NULL), (2, NULL),
        (3, NULL), (3, 'Truman'), (3, NULL), (3, NULL);

我不知道这是否适用于分析(无论如何都不使用 LAG),但这是一种具有一个联接和一个子查询的解决方案。真的很简单。

SELECT  lt.ID ID, lt.Natural_key,
        CASE WHEN lt.Somebody IS NULL
            THEN lt1.Somebody
            ELSE lt.Somebody END SomeBody
  FROM  lag_test lt
  LEFT JOIN lag_test lt1
    ON  lt1.ID =(
        SELECT  MAX( ID )
          FROM  lag_test
         WHERE  Natural_key = lt.Natural_key
           and  ID < lt.ID
           AND  SomeBody IS NOT NULL);

查看SQL Fiddle 沙盒。

【讨论】:

  • 这正是我所要求的,谢谢。示例中的 lag_test “表”实际上是一个非常复杂的查询,我将其重写为 CTE,因此我可以引用两次结果来实施您的建议。
【解决方案2】:

无法测试,但试试这个:

SELECT lt.id, lt.natural_key, l.somebody from lag_test lt inner join (select 
lt.natural_key, lt.somebody from lag_test lt inner join (select MAX(id) as LastID, 
somebody from lag_test WHERE NOT somebody is null GROUP BY somebody) as lson 
lt.id=ls.LastID) as l on lt.natural_key=l.natural_key

可能不是最紧凑的方式,但它对我有用。

这是结果

id  natural_key somebody
----------------------------
1   1   Kirk
2   1   Kirk
3   1   Kirk
4   1   Kirk
5   2   Roybal
6   2   Roybal
7   2   Roybal

【讨论】:

  • 感谢这个有用的答案。它没有直接工作(列引用“某人”是模棱两可的第 3 行:(选择 natural_key,某人来自 lag_test lt 内部连接)。我看到了你要去哪里的策略。这有两个问题:1)我没有t 想回填 1:1:Kirk 行。那个应该仍然是 1:1:NULL。 2)我希望这个问题可以使用 lag() 而不是子查询填充来解决。实际的基本查询极其复杂,有数百万行,因此子查询填充策略需要数小时才能完成。
  • 我想您的真实案例与您发布的案例有所不同。为什么没有工作?
猜你喜欢
  • 1970-01-01
  • 2020-01-26
  • 1970-01-01
  • 2012-07-18
  • 2022-01-13
  • 2020-03-25
  • 1970-01-01
  • 2023-01-16
  • 2023-01-17
相关资源
最近更新 更多