В каком порядке выполняются операции SELECT, JOIN, WHERE, GROUP BY, HAVING в SQL запросе
Что проверяют
Понимаете ли вы логическую модель запроса: какие данные видит каждое предложение и почему алиас из SELECT нельзя использовать в WHERE. Сильный кандидат сразу оговаривает, что это логический порядок, а не план выполнения. Следом дают запрос с ошибкой или спрашивают разницу между WHERE и HAVING.
Как отвечать
Логический порядок обработки:
FROMиJOIN: собирается исходный набор строк, условияONприменяются при соединении;WHERE: фильтрует отдельные строки, агрегатов здесь ещё нет;GROUP BY: строки сворачиваются в группы;HAVING: фильтрует группы, уже доступныCOUNT,SUMи другие агрегаты;SELECT: вычисляются выражения и оконные функции, появляются алиасы;DISTINCT: убираются дубли;ORDER BY: сортировка, алиасы изSELECTздесь уже видны;LIMITиOFFSET: отрезается нужная часть.
Из этого следуют практические вещи. Оконную функцию нельзя поставить в WHERE: row_number() считается позже, поэтому фильтр по ней выносят в подзапрос или CTE. Отдельный QUALIFY, как в Snowflake или BigQuery, есть не везде.
Реальный порядок выбирает планировщик: проталкивает фильтры ближе к таблицам, меняет порядок соединений, берёт индекс. Смотрят его через EXPLAIN ANALYZE. Для Go-разработчика это рутина: запрос из pgx или database/sql с параметрами $1, $2 тормозит, и первым делом вы открываете план, а не переставляете предложения в тексте.
Где спотыкаются
Называют первым SELECT, потому что он первый в тексте. Это самая частая ошибка, и на ней вопрос обычно заканчивается.
Пишут условие на правую таблицу LEFT JOIN в WHERE. Строки без пары получают NULL, фильтр их отбрасывает, и внешнее соединение молча превращается во внутреннее. Такое условие ставят в ON.
Принимают логический порядок за физический и думают, что база сначала соединяет таблицы целиком, а потом фильтрует. Оптимизатор так не работает, и выводы о производительности из такой модели получаются неверными.