Как использовать LAST_VALUE в PostgreSQL? - PullRequest
1 голос
/ 25 сентября 2019

У меня есть небольшая таблица, чтобы попытаться понять, как работает функция LAST_VALUE в PostgreSQL.Это выглядит так:

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

Я хочу использовать LAST_VALUE, чтобы заполнить значение NULL предшествующим ненулевым значением, поэтому результат должен быть следующим:

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

Запрос, который я пытался выполнить:

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

Из того, что я понимаю о функции LAST_VALUE, он берет все строки перед текущей в качестве окна, сортирует их поORDER By вещь, а затем возвращает последний ряд окна.С моим ORDER BY все строки, содержащие NULL, должны быть помещены в верхнюю часть окна, поэтому LAST_VALUE должна возвращать последнее не NULL значение.Но это не так.

Я явно что-то упускаю.Пожалуйста, помогите.

Ответы [ 2 ]

3 голосов
/ 25 сентября 2019

Я не уверен, что 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)

last_value вернет последнее значение текущего кадра.Поскольку вы разбили на части по идентификатору, в текущем кадре всегда будет только одно значение.lag вернет предыдущую строку (по умолчанию) в кадре, что, кажется, именно то, что вы хотите.

Чтобы немного расширить этот ответ, вы можете использовать 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)

Каждая строка - это свой собственный фрейм, поэтому вы не сможете получить значения из других строк..

Если мы не разбиваем на части по идентификатору, но по-прежнему используем ваш порядок, вы можете понять, почему это не сработает для 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)

В этом случае строкаэто был NULL, это первый.По умолчанию last_value будет включать строки до текущей строки, которая в данном случае является просто текущей строкой для идентификатора 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)

Но теперь последняя строкаконец кадра и это явно не то, что вы хотите.Если вы ищете предыдущую строку, выберите lag ().

1 голос
/ 25 сентября 2019

Итак, благодаря объяснениям Джереми и другому посту ( 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;

Этот запрос возвращает ожидаемый результат.

...