HAVING и WHERE
Фильтрация до группировки и после неё: какой раздел видит агрегатную функцию.
«HAVING и WHERE» — бесплатный урок Coding Interview Prep на CoddyKit. Это урок 2 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Coding Interview Prep, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Coding Interview Prep содержит 4 уроков всего.
Вопрос, который Вам зададут
«В чём разница между WHERE и HAVING?» — один из самых популярных вопросов на собеседованиях по SQL. Слабый ответ звучит так: «HAVING используется для агрегатных функций». Сильный ответ объясняет, когда выполняется каждое предложение в конвейере выполнения запроса.
Именно порядок выполнения определяет всё: WHERE фильтрует строки до группировки, а HAVING фильтрует группы после агрегации.
Их место в порядке выполнения
Вспомните логический порядок выполнения запроса:
FROM/JOIN→ формирование набора строкWHERE→ фильтрация отдельных строкGROUP BY→ объединение строк в группыHAVING→ фильтрация группSELECT→ выбор столбцовORDER BY→ сортировка
WHERE выполняется до появления групп, а HAVING — после, поэтому HAVING видит агрегаты, а WHERE — нет.
WHERE не видит агрегаты
Поскольку WHERE выполняется до группировки, значения агрегатных функций ещё недоступны. Запись WHERE COUNT(*) > 5 является синтаксической ошибкой в любой СУБД, соответствующей стандарту.
Интервьюеры намеренно приводят именно эту строку, чтобы проверить, понимаете ли Вы конвейер выполнения. В момент вычисления WHERE агрегат ещё не существует.
-- ERROR: aggregate not allowed in WHERE
SELECT department, COUNT(*)
FROM employees
WHERE COUNT(*) > 5
GROUP BY department;HAVING фильтрует группы
Перенесите условие с агрегатной функцией в HAVING — и оно будет работать, потому что HAVING выполняется после формирования групп и вычисления их агрегатов.
Понимайте это так: «сгруппируйте сотрудников, затем оставьте только те отделы, в которых количество сотрудников превышает пять».
SELECT department, COUNT(*) AS headcount
FROM employees
GROUP BY department
HAVING COUNT(*) > 5;Помещайте фильтры строк в WHERE
Обратная ошибка — фильтровать исходные строки в HAVING. Это часто даёт правильный результат, но является более медленным и вводящим в заблуждение подходом, потому что Вы сгруппировали строки, которые собирались отбросить.
Ориентируйтесь на простое правило: фильтруете по значению исходного столбца → WHERE. Фильтруете по агрегатному значению → HAVING. Ранняя фильтрация строк уменьшает объём данных, который должна обработать группировка.
-- Better: drop inactive rows BEFORE grouping
SELECT department, COUNT(*) AS headcount
FROM employees
WHERE status = 'active'
GROUP BY department
HAVING COUNT(*) > 5;Оба предложения вместе
В полноценном запросе часто используются оба предложения. Сначала WHERE сужает набор строк, затем HAVING оставляет подходящие группы. Чтение запроса сверху вниз соответствует логическому порядку выполнения.
Пример: среди заказов, оформленных в этом году, найдите клиентов, которые потратили в общей сложности более 1000.
SELECT customer_id, SUM(amount) AS total_spent
FROM orders
WHERE order_date >= '2024-01-01'
GROUP BY customer_id
HAVING SUM(amount) > 1000;HAVING для неагрегированных столбцов
HAVING может ссылаться на столбец группировки, а не только на агрегатные функции. Условие HAVING department = 'Sales' допустимо, но бессмысленно: такой фильтр следует поместить в WHERE, чтобы он выполнился раньше.
Если интервьюер покажет Вам HAVING, фильтрующий обычный столбец группировки, ожидаемый комментарий таков: «перенесите его в WHERE для повышения эффективности».
-- Works but inefficient; prefer WHERE department = 'Sales'
SELECT department, COUNT(*)
FROM employees
GROUP BY department
HAVING department = 'Sales';HAVING без GROUP BY
Есть один важный нюанс: HAVING допустим даже без GROUP BY. Вся таблица становится одной неявной группой, и HAVING фильтрует эту единственную группу.
Если условие с агрегатной функцией ложно, результат содержит ноль строк; если истинно — одну строку. На практике это используется редко, но интервьюеры задают такой вопрос, чтобы проверить понимание концепции неявной группы.
-- Returns the count only if the table has > 100 rows
SELECT COUNT(*) AS total
FROM orders
HAVING COUNT(*) > 100;Может ли HAVING использовать псевдоним SELECT?
Как и в других случаях, связанных с областью видимости псевдонимов, диалекты различаются. Postgres и MySQL позволяют HAVING ссылаться на псевдоним SELECT, а SQL Server и Oracle — нет.
Универсальная практика — повторять выражение агрегатной функции в HAVING. Это работает в любой СУБД и помогает избежать неожиданностей на собеседовании по нескольким СУБД.
-- Portable: repeat the aggregate, do not rely on the alias
SELECT region, SUM(amount) AS total
FROM sales
GROUP BY region
HAVING SUM(amount) > 5000;Взгляд с точки зрения производительности
Чтобы произвести хорошее впечатление, свяжите предложения с производительностью: WHERE уменьшает количество строк, которые должен просканировать движок группировки, и может использовать индексы; HAVING выполняется уже для агрегированных групп, поэтому не может уменьшить стоимость группировки.
Главный вывод, который ожидают услышать интервьюеры: переносите каждое условие фильтрации как можно раньше. В HAVING должны находиться только условия, которые действительно зависят от агрегатной функции.
Ответ в одном предложении
Запомните для собеседования: «WHERE фильтрует строки до группировки и не видит агрегаты; HAVING фильтрует группы после агрегации и является единственным предложением, которое может проверять агрегатное значение».
Затем приведите список порядка выполнения — и Ваш ответ будет полным и покажет уровень опытного специалиста.
Быстрая проверка
Определите, в какое предложение следует поместить каждое условие.
Итоги
WHERE: фильтрует строки до GROUP BY, агрегатные функции недопустимы. HAVING: фильтрует группы после агрегации; это единственное место, где допустимо условие с агрегатной функцией.
- Помещайте фильтры по исходным столбцам в WHERE для ускорения работы и использования индексов.
- HAVING может ссылаться на столбцы группировки, но не должен использоваться для обычной фильтрации.
- HAVING работает без GROUP BY, применяясь к неявной группе всей таблицы.
- Для совместимости между диалектами повторяйте выражения агрегатных функций в HAVING.
Часто задаваемые вопросы
Урок «HAVING и WHERE» бесплатный?
Да — полный текст урока «HAVING и WHERE» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Coding Interview Prep, подпишись на CoddyKit PRO. Курс Coding Interview Prep содержит 4 уроков всего.
Чему я научусь в уроке «HAVING и WHERE»?
Фильтрация до группировки и после неё: какой раздел видит агрегатную функцию. Ты практикуешь Coding Interview Prep с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать Coding Interview Prep?
Предыдущий опыт не требуется. Coding Interview Prep на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 2 из 4.
Сколько времени занимает урок «HAVING и WHERE»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке Coding Interview Prep?
Да. Каждый урок Coding Interview Prep включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Правило GROUP BY для столбцов SELECT
- HAVING и WHERE
- Группировка по нескольким столбцам и выражениям
- Подсчёт и фильтрация групп