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