Оператор FILTER
Оператор FILTER может необязательно следовать за агрегатной функцией в операторе SELECT. Это отфильтрует строки данных, которые передаются в агрегатную функцию, так же, как оператор WHERE фильтрует строки, но локально для конкретной агрегатной функции. FILTER в настоящее время не могут использоваться, когда агрегатная функция находится в контексте окна.
Существует несколько ситуаций, когда это полезно, включая оценку нескольких агрегатов с разными фильтрами и создание сводной таблицы набора данных. FILTER предоставляет более чистый синтаксис для сводки данных по сравнению с более традиционным подходом CASE WHEN, обсуждаемым ниже.
Некоторые агрегатные функции также не отфильтровывают нулевые значения, поэтому использование оператора FILTER вернёт корректные результаты, когда подход CASE WHEN не сработает. Это происходит с функциями first и last, которые желательны в не агрегирующей сводке, где цель — просто переориентировать данные в столбцы, а не повторно агрегировать их. FILTER также улучшает обработку нулевых значений при использовании функций list и array_agg, так как подход CASE WHEN включает нулевые значения в результатах, в то время как оператор FILTER их удаляет.
Примеры
Возвращает следующее:
- Общее количество строк.
- Количество строк, где
i <= 5 - Количество строк, где
iнечётное
SELECT
count(*) AS total_rows,
count(*) FILTER (i <= 5) AS lte_five,
count(*) FILTER (i % 2 = 1) AS odds
FROM generate_series(1, 10) tbl(i); | total_rows | lte_five | odds |
|---|---|---|
| 10 | 5 | 5 |
Можно использовать разные агрегатные функции, и разрешено несколько выражений WHERE.
SELECT
sum(i) FILTER (i <= 5) AS lte_five_sum,
median(i) FILTER (i % 2 = 1) AS odds_median,
median(i) FILTER (i % 2 = 1 AND i <= 5) AS odds_lte_five_median
FROM generate_series(1, 10) tbl(i); | lte_five_sum | odds_median | odds_lte_five_median |
|---|---|---|
| 15 | 5.0 | 3.0 |
Оператор FILTER также можно использовать для сводки данных из строк в столбцы. Это статическая сводка, так как столбцы должны быть определены до выполнения запроса в SQL. Однако этот вид оператора можно динамически генерировать в языке хоста, чтобы использовать движок SQL DuckDB для быстрой сводки данных, превышающих объём оперативной памяти.
Сначала сгенерируем примерный набор данных:
CREATE TEMP TABLE stacked_data AS
SELECT
i,
CASE WHEN i <= rows * 0.25 THEN 2022
WHEN i <= rows * 0.5 THEN 2023
WHEN i <= rows * 0.75 THEN 2024
WHEN i <= rows * 0.875 THEN 2025
ELSE NULL
END AS year
FROM (
SELECT
i,
count(*) OVER () AS rows
FROM generate_series(1, 100_000_000) tbl(i)
) tbl; «Сведём» данные по годам (переместим каждый год в отдельный столбец):
SELECT
count(i) FILTER (year = 2022) AS "2022",
count(i) FILTER (year = 2023) AS "2023",
count(i) FILTER (year = 2024) AS "2024",
count(i) FILTER (year = 2025) AS "2025",
count(i) FILTER (year IS NULL) AS "NULLs"
FROM stacked_data; Этот синтаксис даёт те же результаты, что и операторы FILTER выше:
SELECT
count(CASE WHEN year = 2022 THEN i END) AS "2022",
count(CASE WHEN year = 2023 THEN i END) AS "2023",
count(CASE WHEN year = 2024 THEN i END) AS "2024",
count(CASE WHEN year = 2025 THEN i END) AS "2025",
count(CASE WHEN year IS NULL THEN i END) AS "NULLs"
FROM stacked_data; | 2022 | 2023 | 2024 | 2025 | NULLs |
|---|---|---|---|---|
| 25000000 | 25000000 | 25000000 | 12500000 | 12500000 |
Однако, подход CASE WHEN не будет работать как ожидается, при использовании агрегатной функции, которая не игнорирует NULL значения. Функция first относится к этой категории, поэтому FILTER предпочтительнее в этом случае.
«Сведём» данные по годам (переместим каждый год в отдельный столбец):
SELECT
first(i) FILTER (year = 2022) AS "2022",
first(i) FILTER (year = 2023) AS "2023",
first(i) FILTER (year = 2024) AS "2024",
first(i) FILTER (year = 2025) AS "2025",
first(i) FILTER (year IS NULL) AS "NULLs"
FROM stacked_data; | 2022 | 2023 | 2024 | 2025 | NULLs |
|---|---|---|---|---|
| 1474561 | 25804801 | 50749441 | 76431361 | 87500001 |
Это приведет к NULL значениям, всякий раз, когда первое вычисление оператора CASE WHEN возвращает NULL:
SELECT
first(CASE WHEN year = 2022 THEN i END) AS "2022",
first(CASE WHEN year = 2023 THEN i END) AS "2023",
first(CASE WHEN year = 2024 THEN i END) AS "2024",
first(CASE WHEN year = 2025 THEN i END) AS "2025",
first(CASE WHEN year IS NULL THEN i END) AS "NULLs"
FROM stacked_data; | 2022 | 2023 | 2024 | 2025 | NULLs |
|---|---|---|---|---|
| 1228801 | NULL | NULL | NULL | NULL |
Синтаксис агрегатных функций (включая оператор FILTER)
© Copyright 2018–2024 Stichting DuckDB Foundation
Licensed under the MIT License.
https://duckdb.org/docs/sql/query_syntax/filter.html