0Pricing
SQL Interview Prep · Урок

HAVING и WHERE

Фильтрация до группировки и после неё: какой раздел видит агрегатную функцию.

«HAVING и WHERE» — бесплатный урок SQL Interview Prep на CoddyKit. Это урок 2 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения SQL Interview Prep, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс SQL 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) и разблокировать остальной курс SQL Interview Prep, подпишись на CoddyKit PRO. Курс SQL Interview Prep содержит 4 уроков всего.

Чему я научусь в уроке «HAVING и WHERE»?

Фильтрация до группировки и после неё: какой раздел видит агрегатную функцию. Ты практикуешь SQL Interview Prep с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать SQL Interview Prep?

Предыдущий опыт не требуется. SQL Interview Prep на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 2 из 4.

Сколько времени занимает урок «HAVING и WHERE»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке SQL Interview Prep?

Да. Каждый урок SQL Interview Prep включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Правило GROUP BY для столбцов SELECT
  2. HAVING и WHERE
  3. Группировка по нескольким столбцам и выражениям
  4. Подсчёт и фильтрация групп
← Назад к SQL Interview Prep