В оконном отчёте нужно посчитать агрегат только по части строк раздела, не убирая остальные строки из резул...

В оконном отчёте нужно посчитать агрегат только по части строк раздела, не убирая остальные строки из результата. Какой механизм задаёт такое ограничение?

Проходите собеседования с ИИ помощником Hintsage

Краткий ответ

Для этого используется FILTER у агрегатной функции в оконном выражении. Он ограничивает строки, которые участвуют в конкретном агрегате, но не изменяет сам раздел окна и не удаляет строки из итогового результата.

Исторический контекст

Обычная агрегация через GROUP BY сворачивает несколько строк в одну, а оконные функции позволяют вычислять показатели по связанному набору строк, сохраняя каждую исходную строку. Затем появилась потребность считать несколько условных показателей над одним и тем же окном без изменения набора строк через WHERE.

FILTER решает именно эту задачу: условие относится к конкретному агрегату, а не ко всему запросу. В диалектах без поддержки FILTER аналог часто выражают через условную логику внутри агрегата.

Постановка проблемы

Предположим, в отчёте по клиентам нужно показать каждую операцию, общую сумму всех операций клиента и отдельно сумму только оплаченных операций. Фильтрация через WHERE убрала бы неоплаченные строки из результата и изменила бы состав окна для всех вычислений.

Это может привести к неверным итогам: например, общее число операций начнёт учитывать только оплаченные операции. Кроме того, нельзя будет вывести неоплаченную операцию рядом с рассчитанными показателями.

Подробное решение

FILTER применяется внутри конкретного агрегата. Сначала для каждой строки определяется её оконный раздел и, если задано, оконная рамка; затем агрегат учитывает только те строки из доступного набора, которые проходят условие FILTER.

SELECT customer_id, operation_id, SUM(amount) OVER ( PARTITION BY customer_id ) AS all_amount, SUM(amount) FILTER (WHERE status = 'paid') OVER ( PARTITION BY customer_id ) AS paid_amount FROM operations;

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

WHERE действует раньше оконных функций и формирует общий набор строк запроса. FILTER действует локально для одного агрегата. Поэтому два агрегата в одном SELECT могут использовать разные подмножества одного окна.

Для агрегатов, возвращающих сумму или среднее, отсутствие подходящих строк обычно приводит к NULL, а не к нулю. Для COUNT результатом будет ноль. Если требуется отображать ноль вместо NULL, используют COALESCE, но это уже отдельное преобразование результата.

Эквивалент через условное выражение часто выглядит как суммирование значения только при выполнении условия. Однако FILTER яснее показывает намерение и позволяет отделить условие агрегата от вычисляемого значения. Поддержка FILTER зависит от конкретной СУБД, поэтому при переносе запроса между диалектами нужно проверить совместимость.

Ситуация из практики

В отчёте по операциям клиента требовались одновременно общий оборот, оборот по успешным операциям и число всех операций. Вариант с WHERE status = 'paid' был отклонён: он скрывал неуспешные операции и делал общий оборот условным.

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

Выбрали FILTER в СУБД, которая его поддерживала. Все показатели вычислялись над одним оконным разделом, при этом каждое условие было привязано только к своему агрегату. Это сохранило строки отчёта и исключило смешение условий между метриками.

Что кандидаты часто упускают

  1. Изменяет ли FILTER состав оконного раздела?

Нет. Раздел, заданный через PARTITION BY, формируется независимо от FILTER. Условие FILTER влияет только на строки, передаваемые конкретной агрегатной функции, поэтому другая оконная функция в том же SELECT может обработать полный раздел.

  1. Чем FILTER отличается от WHERE в оконном запросе?

WHERE исключает строки из общего набора до вычисления оконных функций. Такие строки исчезают из результата и не участвуют ни в одном окне. FILTER не удаляет строки и не меняет доступный набор для других выражений, а ограничивает только один агрегат.

  1. Можно ли применять FILTER к любой оконной функции?

Нет, его назначение — фильтрация входа агрегатной функции, в том числе агрегата, вызванного как оконная функция. Ранжирующие функции вроде ROW_NUMBER или RANK не являются агрегатами и не получают такой фильтр напрямую. Для них обычно меняют набор строк до вычисления функции, используют условную логику вокруг результата или перестраивают запрос; точные возможности зависят от СУБД.