Порядок выполнения SQL-запроса
Почему SQL пишется в одном порядке, а PostgreSQL логически обрабатывает его в другом
Теория
Из-за этого часто появляются вопросы, которые сначала могут показаться странными. Почему псевдоним из SELECT нельзя использовать в WHERE, но можно использовать в ORDER BY? Почему агрегатная функция не работает в WHERE, а в HAVING уже работает? Почему LIMIT не берёт «первые попавшиеся» строки, если перед ним стоит сортировка?
Ответ везде один и тот же: у SQL есть логический порядок выполнения.
Синтаксический порядок написания операторов в запросе такой:
• SELECT;
• FROM;
• WHERE;
• GROUP BY;
• HAVING;
• ORDER BY;
• LIMIT.
А если смотреть глазами PostgreSQL, такой:
• сначала FROM;
• потом WHERE;
• потом GROUP BY;
• потом HAVING;
• потом SELECT;
• потом ORDER BY;
• и только в конце LIMIT.
Давайте представим это как конвейер. В начале на ленту кладут целую таблицу. Потом с неё убирают лишние строки. Потом при необходимости собирают оставшиеся строки в группы. Затем отбрасывают лишние группы. Потом уже решают, какие именно столбцы показать в результате. И только в самом конце этот результат сортируют и, если нужно, обрезают по количеству строк.
Если разобраться как этот конвейер работает, половина SQL загадок перестанет быть таковыми. Псевдоним из SELECT не работает в WHERE просто потому, что до шага SELECT СУБД ещё даже не дошла. А в ORDER BY он уже виден, потому что итоговый набор столбцов к этому моменту существует.
Это одна из самых полезных тем. Понимание этой темы позволит взглянуть на SQL-запрос как на единую логичную конструкцию, состоящую из логических операций, где каждая текущая операция передаёт результат своей работы следующей операции.
Синтаксис
SELECT column_list
FROM table_name
WHERE condition
GROUP BY group_columns
HAVING group_condition
ORDER BY sort_columns
LIMIT row_count;
FROM -> WHERE -> GROUP BY -> HAVING -> SELECT -> ORDER BY -> LIMIT
SQL
Ниже представлена информация, позволяющая понять как SQL логически обрабатывается внутри PostgreSQL.
| Часть запроса | Что делает | Когда реально срабатывает |
|---|---|---|
| FROM | Определяет источник данных | Самый ранний шаг |
| WHERE | Фильтрует строки | Сразу после FROM |
| GROUP BY | Собирает строки в группы | После фильтрации строк |
| HAVING | Фильтрует уже готовые группы | После GROUP BY |
| SELECT | Формирует итоговые столбцы результата | После строк и групп |
| ORDER BY | Сортирует готовый результат | После SELECT |
| LIMIT | Оставляет нужное количество строк | Самый последний шаг |
1. Писать сверху вниз и исполнять сверху вниз — не одно и то же
Запрос на экране начинается с SELECT, потому что человеку так удобнее читать. Но PostgreSQL не может сначала выбрать столбцы, пока ещё не знает, из каких строк вообще будет строиться результат. Поэтому логический запуск начинается с FROM и дальше движется по конвейеру.
2. WHERE работает со строками, а HAVING — уже с группами
WHERE отбрасывает строки до группировки. HAVING включается позже, когда группы уже собраны. Поэтому агрегат вроде COUNT(*) в WHERE неуместен, а в HAVING — это как раз его место.
3. Псевдоним из SELECT не живёт в прошлом
Если столбец получил имя через AS внутри SELECT, это имя появляется только на шаге формирования результата. Именно поэтому такое имя уже может использовать ORDER BY, но WHERE его ещё не видит.
4. С GROUP BY есть тонкий нюанс
В PostgreSQL группировку иногда можно записать по псевдониму или по номеру столбца. Это удобство синтаксиса, а не повод думать, будто GROUP BY логически выполняется после SELECT.
5. LIMIT ограничивает готовый результат
Если перед LIMIT стоит сортировка, сначала PostgreSQL отсортирует строки, а потом возьмёт нужное количество строк сверху. Поэтому ORDER BY salary DESC LIMIT 3 и означает «три самые большие зарплаты», а не просто «какие-то три строки».
Примеры
1. Сначала фильтруем строки, потом уже показываем столбцы
🔄 Попробуйте изменить запрос:
- • Уберите LIMIT 5 и посмотрите, что изменится
- • Замените порог 90000 на 80000
2. Псевдоним работает в ORDER BY
🔄 Попробуйте изменить запрос:
- • Попробуйте отсортировать по discounted_price ASC
3. В WHERE выражение приходится повторять
🔄 Попробуйте изменить запрос:
- • Попробуйте в уме представить, что будет, если написать в WHERE псевдоним raised_salary, а затем напишите и проверьте
4. WHERE работает до группировки, HAVING — после
🔄 Попробуйте изменить запрос:
- • Замените порог в WHERE с 10000 на 50000
- • Поменяйте условие HAVING COUNT(*) >= 2 на псевдоним products_count >= 2
5. LIMIT ограничивает отсортированный результат
🔄 Попробуйте изменить запрос:
- • Замените LIMIT 3 на LIMIT 5
- • Попробуйте убрать сортировку и посмотрите как отличается результат
Типичные ошибки
Читают SQL как будто он исполняется сверху вниз
На экране запрос действительно начинается с SELECT, но логически PostgreSQL сначала работает с FROM и WHERE. Если об этом забыть, сразу появляются странные ожидания от псевдонимов, агрегатов и сортировки.
Пишут псевдоним из SELECT в WHERE
Это одна из самых частых ловушек. До шага WHERE псевдоним ещё не появился, поэтому фильтр его не видит. Если нужно фильтровать по выражению, это выражение приходится повторять.
Путают WHERE и HAVING
WHERE фильтрует строки до группировки. HAVING фильтрует уже готовые группы. Можно прямо так просто это и запомнить.
Думают, что LIMIT выбирает строки раньше сортировки
Нет. Если перед LIMIT есть ORDER BY, сначала результат сортируется, а уже потом ограничивается.
Делают выводы из удобства синтаксиса, а не из логики выполнения
Например, PostgreSQL позволяет использовать псевдоним в GROUP BY. Это не значит, что группировка выполняется после SELECT. Это всего лишь удобство записи.
Практика
Проверь себя
Ответьте на вопросы, чтобы закрепить материал: