Объясните механизм: почему имя, присвоенное вычисляемому столбцу в списке выбора, обычно недоступно при фильтрации строк в том же запросе?
Псевдоним вычисляемого столбца обычно недоступен в фильтре строк, потому что в логическом порядке обработки запроса фильтрация выполняется раньше формирования списка выбираемых столбцов. На момент фильтрации такого имени ещё не существует.
Чтобы использовать вычисление при фильтрации, его повторяют, выносят во вложенный запрос или Common Table Expression. Наиболее переносимый вариант — сначала сформировать именованный столбец во внутреннем запросе, а затем отфильтровать результат во внешнем.
SQL создавался как декларативный язык: разработчик описывает требуемый результат, а не последовательность физических операций. Для однозначного определения результата языку нужен логический порядок обработки частей запроса, независимый от фактического плана выполнения.
Такой подход позволяет оптимизатору менять физический порядок операций, сохраняя логическую семантику. Поэтому текст запроса нельзя интерпретировать просто сверху вниз: наличие имени в одной части запроса зависит от того, на каком логическом этапе оно появляется.
Предположим, вычисляемый показатель получает имя, после чего разработчик пытается использовать это имя для отбора строк. Возникает ошибка разрешения столбца либо поведение, зависящее от конкретной СУБД.
Неверное исправление может привести к дублированию сложного выражения, расхождению условий после изменения формулы или снижению читаемости. Попытка заменить фильтр строк фильтром групп также меняет смысл запроса: группировка и фильтрация агрегатов работают не так, как отбор отдельных строк.
Упрощённый логический порядок обработки выглядит так: источник данных, фильтрация строк, группировка, фильтрация групп, формирование списка выбираемых столбцов, устранение дубликатов, сортировка и ограничение результата. Физический план может выполнять операции в другом порядке, но результат должен соответствовать этой логике.
Псевдоним вычисляемого столбца создаётся на этапе формирования списка выбираемых столбцов. Фильтр строк к этому моменту уже должен быть вычислен, поэтому он не может надёжно ссылаться на такой псевдоним.
Во внутреннем запросе вычисляется и именуется показатель, а внешний запрос получает его как обычный столбец и может фильтровать по нему. Альтернатива — повторить выражение непосредственно в условии; это иногда проще, но повышает риск рассинхронизации формулы.
Ссылки на псевдонимы в сортировке часто поддерживаются, поскольку сортировка логически выполняется после формирования результата. Однако переносимость таких возможностей зависит от СУБД и конкретного контекста, поэтому для фильтрации лучше опираться на вложенный запрос или Common Table Expression.
В отчёте рассчитывалась итоговая стоимость заказа с учётом наценки, и требовалось оставить только заказы выше порога. Разработчик повторил выражение в фильтре: решение работало, но после изменения формулы в списке выбора условие забыли обновить, и отчёт начал показывать несогласованные значения.
Рассматривались три варианта. Повторение выражения не требовало дополнительного уровня запроса, но ухудшало поддержку. Перенос фильтра в фильтр групп был неприменим, поскольку требовалась проверка отдельных заказов, а не агрегированных групп. Вложенный запрос добавлял уровень композиции, зато давал вычисленному показателю явное имя и единый источник истины.
Выбрали вложенный запрос. После этого формула находилась в одном месте, внешний фильтр работал с обычным столбцом, а изменение расчёта не требовало синхронного редактирования нескольких фрагментов. Оптимизатор при необходимости мог преобразовать такую конструкцию без изменения логического результата.
Во многих СУБД — да, поскольку сортировка логически выполняется после формирования списка выбираемых столбцов. Это не означает, что тот же псевдоним доступен во всех частях запроса: фильтрация строк выполняется раньше. Для переносимого SQL необходимо учитывать диалект и не распространять правило сортировки на фильтры автоматически.
Фильтр строк отбрасывает отдельные записи до группировки и влияет на состав групп и агрегаты. Фильтр групп применяется после группировки и оценивает уже сформированные группы, часто с использованием агрегатных результатов. Поэтому эти два фильтра взаимозаменяемы только в специальных случаях, когда доказана эквивалентность условий.
Оконная функция вычисляется на более позднем этапе, чем фильтр строк, поэтому её результат нельзя надёжно использовать в том же фильтре. Решение аналогично: вычислить оконный показатель во внутреннем запросе или Common Table Expression, а затем применить внешний фильтр. Это также сохраняет границу между вычислением результата и отбором уже вычисленных значений.