SELECT
date_trunc('day', created_at) AS day,
COUNT(*) AS orders_total,
COUNT(*) FILTER (WHERE status = 'paid') AS paid,
COUNT(*) FILTER (WHERE status = 'refunded') AS refunded
FROM orders
GROUP BY 1
ORDER BY 1;
Результат:
day | orders_total | paid | refunded -----------+--------------+------+--------- 2026-07-10 | 320 | 298 | 12 2026-07-11 | 341 | 305 | 9
SELECT
user_id,
SUM(amount) AS revenue_total,
SUM(amount) FILTER (WHERE created_at >= DATE '2026-07-01') AS revenue_july
FROM payments
GROUP BY user_id;
Результат:
user_id | revenue_total | revenue_july
--------+---------------+-------------
17 | 11940 | 3998
42 | 5997 | 1999
SELECT
COUNT(DISTINCT user_id) FILTER (WHERE event_name = 'purchase') AS buyers
FROM events;
Результат:
buyers
------
46
Порешать SQL-задачи в тренажёре →
Спрашивают при построении когорт и pivot-отчётов (paid/refunded/pending в одной строке). Идёт в паре с GROUP BY и оконными функциями; классический follow-up — переписать то же самое через CASE для MySQL.
Нет, MySQL и MariaDB не понимают FILTER. Там условную агрегацию делают через агрегат от CASE: COUNT(CASE WHEN status = 'paid' THEN 1 END) или SUM(CASE WHEN … THEN amount ELSE 0 END). FILTER доступен в PostgreSQL (9.4+) и SQLite (3.30+).
FILTER читается как обычное условие и не требует оборачивать выражение в CASE. Главное практическое преимущество — он корректно работает с COUNT(DISTINCT x) FILTER (WHERE …), что через CASE выразить неудобно. В остальном результат тот же, что у COUNT(CASE …).
Да, в PostgreSQL допустимо agg(...) FILTER (WHERE ...) OVER (...) — фильтр применяется к агрегату, вычисляемому как оконный. Но FILTER работает только с агрегатными функциями; к ranking-функциям вроде ROW_NUMBER() или LAG() его прицепить нельзя.
COALESCE · NULLIF · CASE WHEN · STRING_AGG · COUNT · SUM OVER
Открыть SQL-тренажёр (565 задач) →