В каком порядке выполняются операции SELECT, JOIN, WHERE, GROUP BY, HAVING в SQL запросе
Что проверяют
Различаете ли вы порядок записи запроса и логический порядок его выполнения. От этого зависят ответы на практические вопросы: почему псевдоним из SELECT не виден в WHERE, почему агрегат нельзя фильтровать в WHERE. Следом часто дают запрос с LEFT JOIN и условием в WHERE и просят найти ошибку.
Как отвечать
Логический порядок обработки:
- FROM и JOIN: собирается исходный набор строк, условия
ONприменяются при соединении; - WHERE: фильтр отдельных строк, агрегатов ещё нет;
- GROUP BY: строки сворачиваются в группы;
- HAVING: фильтр групп, здесь уже доступны
COUNT,SUMи остальные агрегаты; - SELECT: вычисляются выражения, агрегаты и оконные функции, появляются псевдонимы;
- DISTINCT: удаляются дубли;
- ORDER BY: сортировка, поэтому псевдонимы из
SELECTздесь уже работают; - LIMIT и OFFSET (или
FETCH FIRST): отсечение строк.
Сразу оговоритесь, что это логическая модель, а не физический план. Оптимизатор вправе переставить соединения, протолкнуть фильтр ближе к таблице, взять индекс для сортировки. Результат при этом обязан совпасть с логическим порядком, а реальные шаги показывает EXPLAIN.
Для Java-разработчика это важно и при работе через Hibernate: JPQL подчиняется той же логике, а сгенерированный SQL стоит посмотреть в логах, прежде чем искать ошибку в маппинге.
Где спотыкаются
Используют псевдоним из SELECT в HAVING или WHERE, например HAVING employee_count > 10. MySQL такое допускает, PostgreSQL и SQL Server нет, нужно повторить COUNT(*).
Ставят условие на правую таблицу LEFT JOIN в WHERE. У строк без пары там NULL, фильтр их отбрасывает, и соединение фактически становится внутренним. Условие нужно перенести в ON.
Пытаются отфильтровать по оконной функции прямо в WHERE, например оставить ROW_NUMBER() = 1. Окна вычисляются на шаге SELECT, после WHERE, поэтому нужен подзапрос или CTE.