Может ли PostgreSQL вернуть список значений с последними значениями поверх предыдущих? - PullRequest
2 голосов
/ 19 марта 2020

В таблице ниже представлены события принятия решения о лицензии данного файла.

Я хочу получить список лицензий для файла. Если для столбца «Удалено» события задано значение «Ложь», добавляется лицензия, но не более одной лицензии для каждого файла. Если для столбца Removed установлено значение True, все предыдущие события добавления для конкретной лицензии отменяются. Событие, совершенное позже, имеет приоритет над предыдущими.

Для приведенных ниже событий я хочу вернуть список [A, B]. Лицензия A имеет события add, remove, add, поэтому она возвращается. В лицензии B есть события add, add, поэтому она возвращается, но не дублируется. Лицензия C имеет события add, add, remove, поэтому она не удаляется, поскольку последнее удаление аннулирует оба add-события.

Возможно ли это сделать с помощью запроса PostgreSQL, или мне нужно обрабатывать данные потом?

+------+---------+---------+
| Time | License | Removed |
+------+---------+---------+
|    1 | A       | False   |
|    2 | A       | True    |
|    3 | A       | False   |
|    4 | B       | False   |
|    5 | B       | False   |
|    6 | C       | False   |
|    7 | C       | False   |
|    8 | C       | True    |
+------+---------+---------+

Ответы [ 3 ]

1 голос
/ 19 марта 2020

Если я правильно понимаю, вы хотите событие last для каждой лицензии. Это довольно просто, если вы знаете, как работают оконные функции: вы можете разбить таблицу, используя windows, а затем работать внутри каждого окна, сортируя и т. Д. c. В этом случае вы хотите разделить на лицензии, затем отсортировать по времени (по убыванию) и, наконец, выбрать самую последнюю запись каждого окна:

SELECT "License", "Removed" FROM (
    SELECT *, rank() OVER (PARTITION BY "License" ORDER BY "Time" DESC)) X
 WHERE rank = 1

Если вы хотите лучше понять, как это работает, попробуйте выполнение внутреннего SELECT самостоятельно.

1 голос
/ 19 марта 2020

Для этого вы можете использовать агрегацию:

select license
from t
group by license
having max(time) = max(time) filter (where not removed);

Предложение having проверяет, что максимальное время для лицензии не "удалено".

Все три ответа вполне разумно. В общем, distinct on предпочтительнее в Postgres, чем row_number() для извлечения одной строки. Я предлагаю это, если у вас есть строгое отвращение к подзапросам.

1 голос
/ 19 марта 2020

Вы можете использовать distinct on в подзапросе, чтобы отфильтровать последнюю запись для каждой лицензии, а затем отфильтровать те, которые удалены:

select license
from (select distinct on (license) t.* from mytable t order by license, time desc) t
where not removed
...