Одна из самых полезных тем в SQL — это порядок выполнения запроса. Именно она помогает понять:
- почему
WHEREне видит агрегаты - почему
HAVINGработает послеGROUP BY - почему алиас из
SELECTнельзя использовать везде - почему итог запроса иногда не совпадает с тем, что ты ожидал
И это же одна из любимых тем на собеседованиях.
Почему это важно
Когда мы читаем SQL, мы видим его сверху вниз:
SELECT ...
FROM ...
WHERE ...
GROUP BY ...
HAVING ...
ORDER BY ...
Но выполняется он не в этом порядке.
SQL сначала собирает и фильтрует данные, потом группирует, потом считает агрегаты, потом формирует итоговый вывод.
Базовый порядок выполнения SQL-запроса
Если упростить, то чаще всего порядок такой:
FROMJOINWHEREGROUP BY- агрегатные функции
HAVING- оконные функции
SELECTDISTINCTORDER BYLIMIT
Это не единственная возможная формулировка в мире, но для практики и собеседований это очень хорошая рабочая схема.
1. FROM — откуда берем данные
На этом этапе SQL определяет, из какой таблицы или подзапроса будет строиться результат.
FROM orders
Если таблиц несколько, то дальше подключаются JOIN.
2. JOIN — как соединяются таблицы
После FROM SQL начинает соединять таблицы.
FROM orders o
LEFT JOIN customers c
ON o.customer_id = c.customer_id
Именно здесь часто возникают:
- дубли строк
- неожиданное увеличение результата
- потеря строк при неправильном типе join
Поэтому если после JOIN у тебя “внезапно все умножилось”, искать проблему нужно именно здесь.
3. WHERE — фильтрация строк до группировки
WHERE работает по обычным строкам, до агрегирования.
WHERE status = 'paid'
Именно поэтому в WHERE нельзя писать:
WHERE SUM(amount) > 1000
На этом этапе SUM(amount) еще не существует.
4. GROUP BY — группировка
После фильтрации SQL собирает строки в группы.
GROUP BY customer_id
Теперь вместо набора отдельных строк у нас появляются группы, по которым уже можно считать агрегаты.
5. Агрегатные функции — SUM, COUNT, AVG и другие
На этом этапе вычисляются агрегаты:
SUM()COUNT()AVG()MIN()MAX()
Например:
SELECT
customer_id,
SUM(amount) AS total_amount
FROM orders
GROUP BY customer_id
SUM(amount) появляется только после того, как группы уже сформированы.
6. HAVING — фильтрация уже сгруппированного результата
HAVING работает после GROUP BY и после расчета агрегатов.
HAVING SUM(amount) > 1000
Вот почему SUM(amount) можно использовать в HAVING, но нельзя в WHERE.
Запоминалка:
WHERE— фильтр строкHAVING— фильтр групп
7. Оконные функции
После агрегации и HAVING обычно уже можно применять оконные функции.
Например:
SELECT
customer_id,
total_amount,
RANK() OVER (ORDER BY total_amount DESC) AS rank_num
FROM (
SELECT
customer_id,
SUM(amount) AS total_amount
FROM orders
GROUP BY customer_id
) t
Оконные функции работают над уже подготовленным набором строк.
Поэтому они логически идут позже, чем GROUP BY и агрегаты.
8. SELECT — формирование итогового набора столбцов
Только теперь SQL окончательно собирает то, что попадет в вывод.
SELECT
customer_id,
SUM(amount) AS total_amount
Вот почему алиас, который создается в SELECT, не всегда доступен раньше.
Например, нельзя ожидать, что алиас уже существует в WHERE.
9. DISTINCT — удаление дублей
Если используется DISTINCT, он применяется уже к итоговому набору строк.
SELECT DISTINCT customer_id
FROM orders
То есть сначала формируется результат, потом из него убираются повторяющиеся строки.
10. ORDER BY — сортировка
После этого SQL сортирует итоговый результат.
ORDER BY total_amount DESC
Вот здесь алиасы из SELECT обычно уже можно использовать.
11. LIMIT — ограничение числа строк
И только в самом конце SQL обрезает результат по количеству строк.
LIMIT 10
Сначала сортировка, потом ограничение.
Именно поэтому:
ORDER BY revenue DESC
LIMIT 10
означает “сначала найти самых больших, потом взять 10”.
Частая ошибка №1: путать порядок написания и порядок выполнения
Мы видим запрос сверху вниз, но SQL работает не так.
Из-за этого и появляются вопросы:
- почему
WHEREне видитSUM() - почему
HAVINGработает - почему после
JOINрезультат уже изменился, хотяSELECTеще даже не сформировался
Частая ошибка №2: не понимать, где появляются дубликаты
Если число строк выросло, это почти никогда не из-за SELECT или ORDER BY.
Обычно причины:
JOIN- неправильный ключ соединения
- many-to-many логика
Порядок выполнения помогает быстро локализовать проблему.
Частая ошибка №3: алиасы из SELECT
Например:
SELECT
amount * 1.2 AS amount_with_tax
FROM orders
WHERE amount_with_tax > 100
Во многих СУБД это не сработает, потому что WHERE выполняется раньше SELECT.
Нужно писать либо выражение целиком:
WHERE amount * 1.2 > 100
либо выносить это в подзапрос / CTE.
Удобная схема для запоминания
Можно запомнить так:
- собрать строки
- соединить таблицы
- отфильтровать строки
- сгруппировать
- посчитать агрегаты
- отфильтровать группы
- посчитать окна
- показать нужные столбцы
- убрать дубли
- отсортировать
- обрезать результат
Это уже достаточно, чтобы уверенно отвечать на большую часть вопросов по теме.
Итог
Порядок выполнения SQL-запроса — это одна из тех тем, которые резко повышают качество понимания SQL.
Если ты держишь в голове эту логику, тебе проще:
- писать запросы без ошибок
- понимать поведение
WHEREиHAVING - объяснять результат на собеседованиях
- находить причины дублей после
JOIN
Именно поэтому эту тему стоит не просто “знать”, а реально понимать.