如何在 PostgreSQL 中使用 LAST_VALUE?

the*_*n29 6 sql postgresql window-functions

我有一个小表格来尝试了解该LAST_VALUE函数在 PostgreSQL 中的工作原理。它看起来像这样:

 id | value
----+--------
  0 | A
  1 | B
  2 | C
  3 | D
  4 | E
  5 | [null]
  6 | F
Run Code Online (Sandbox Code Playgroud)

我想要做的是使用LAST_VALUE先行的非NULL值来填充NULL值,所以结果应该是这样的:

 id | value
----+--------
  0 | A
  1 | B
  2 | C
  3 | D
  4 | E
  5 | E
  6 | F

Run Code Online (Sandbox Code Playgroud)

我试图完成的查询是:

SELECT LAST_VALUE(value)
OVER (PARTITION BY id ORDER BY case WHEN value IS NULL THEN 0 ELSE 1 END ASC)
FROM test;
Run Code Online (Sandbox Code Playgroud)

根据我对LAST_VALUE函数的理解,它将当前行之前的所有行作为一个窗口,按照ORDER By事物对它们进行排序,然后返回窗口的最后一行。使用 my ORDER BY,所有包含 NULL 的行都应该放在窗口的顶部,因此LAST_VALUE应该返回最后一个非 NULL 值。但事实并非如此。

我显然错过了一些东西。请帮忙。

Jer*_*emy 9

我不确定 last_value 会做你想做的事。最好使用滞后:

select id,
coalesce(value, lag(value) OVER (order by id))
FROM test;
 id | coalesce
----+----------
  0 | A
  1 | B
  2 | C
  3 | D
  4 | E
  5 | E
  6 | F
(7 rows)
Run Code Online (Sandbox Code Playgroud)

last_value 将返回当前帧的最后一个值。由于您按 id 分区,因此当前帧中只有一个值。滞后将返回框架中的前一行(默认情况下),这似乎正是您想要的。

为了稍微扩展这个答案,您可以使用 row_number() 来让您对正在查看的框架有一个很好的了解。对于您提出的解决方案,当您按 id 分区时,请查看每行的行号:

SELECT id, row_number() OVER (PARTITION BY id ORDER BY case WHEN value IS NULL THEN 0 ELSE 1 END ASC)
FROM test;
 id | row_number
----+------------
  0 |          1
  1 |          1
  2 |          1
  3 |          1
  4 |          1
  5 |          1
  6 |          1
(7 rows)
Run Code Online (Sandbox Code Playgroud)

每行都是它自己的框架,因此您将无法从其他行获取任何值。

如果我们不按 id 分区,但仍然使用您的顺序,您可以看到为什么这仍然不适用于 last_value:

 SELECT id, row_number() OVER (ORDER BY case WHEN value IS NULL THEN 0 ELSE 1 END ASC, id)
FROM test;
 id | row_number
----+------------
  5 |          1
  0 |          2
  1 |          3
  2 |          4
  3 |          5
  4 |          6
  6 |          7
(7 rows)
Run Code Online (Sandbox Code Playgroud)

在这种情况下,为 NULL 的行是第一行。默认情况下,last_value 将包含直到当前行的行,在这种情况下,它只是 id 5 的当前行。您可以包含框架中的所有行:

SELECT id, 
  row_number() OVER (ORDER BY case WHEN value IS NULL THEN 0 ELSE 1 END ASC, 
id ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING), 
  last_value(value) OVER (ORDER BY case WHEN value IS NULL THEN 0 ELSE 1 END ASC, id ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)
FROM test;
 id | row_number | last_value
----+------------+------------
  5 |          1 | F
  0 |          2 | F
  1 |          3 | F
  2 |          4 | F
  3 |          5 | F
  4 |          6 | F
  6 |          7 | F
(7 rows)
Run Code Online (Sandbox Code Playgroud)

但是现在最后一行是帧的结尾,这显然不是您想要的。如果您要查找上一行,请选择 lag()。


the*_*n29 2

所以,多亏了杰里米的解释和另一篇文章(PostgreSQL的last_value忽略空值)我终于弄清楚了:

SELECT id, value, first_value(value) OVER (partition by t.isnull) AS new_val
FROM(
    SELECT id, value, SUM (CASE WHEN value IS NOT NULL THEN 1 END) OVER (ORDER BY id) AS isnull
    FROM test) t;
Run Code Online (Sandbox Code Playgroud)

该查询返回我期望的结果。