← Назад к вопросам
В чем разница между HAVING и WHERE в SQL?
2.0 Middle🔥 241 комментариев
#Базы данных и SQL
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI28 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Разница между HAVING и WHERE в SQL
Это один из самых частых вопросов, который выявляет, насколько кандидат глубоко понимает SQL. WHERE и HAVING работают на разных стадиях выполнения query, и это критично для производительности и правильности результатов.
Порядок выполнения SQL Query
-- Важно помнить этот порядок!
SELECT column1, COUNT(*) as count -- 5. SELECT (выбираем колонки)
FROM users -- 1. FROM (откуда берем данные)
WHERE age > 18 -- 2. WHERE (фильтруем СТРОКИ)
GROUP BY country -- 3. GROUP BY (группируем)
HAVING COUNT(*) > 10 -- 4. HAVING (фильтруем ГРУППЫ)
ORDER BY count DESC -- 6. ORDER BY (сортируем результат)
LIMIT 10; -- 7. LIMIT (ограничиваем результат)
Ключевой момент: WHERE выполняется ДО GROUP BY, а HAVING — ПОСЛЕ.
WHERE — фильтрация строк
-- WHERE фильтрует ОТДЕЛЬНЫЕ строки из таблицы
-- Работает с исходными данными ДО группировки
-- Пример 1: простая фильтрация
SELECT * FROM users WHERE age > 18;
-- Результат: все строки, где возраст больше 18
-- Пример 2: WHERE с GROUP BY
SELECT
country,
COUNT(*) as user_count
FROM users
WHERE age > 18 -- ← WHERE: фильтруем ДО группировки
GROUP BY country;
-- Процесс:
-- 1. Берем все строки из users
-- 2. Фильтруем: оставляем только age > 18
-- 3. Группируем по country
-- 4. Считаем количество в каждой группе
HAVING — фильтрация групп
-- HAVING фильтрует ГРУППЫ, а не строки
-- Работает с результатами GROUP BY
-- Может использовать агрегатные функции (COUNT, SUM, AVG, MIN, MAX)
-- Пример 1: простая фильтрация групп
SELECT
country,
COUNT(*) as user_count
FROM users
GROUP BY country
HAVING COUNT(*) > 100; -- ← HAVING: оставляем только группы с >100 пользователей
-- Процесс:
-- 1. Берем все строки
-- 2. Группируем по country
-- 3. Считаем COUNT(*) для каждой группы
-- 4. Фильтруем: оставляем только группы где COUNT(*) > 100
-- Пример 2: HAVING с несколькими условиями
SELECT
country,
COUNT(*) as user_count,
AVG(age) as avg_age
FROM users
GROUP BY country
HAVING
COUNT(*) > 50 -- В стране более 50 пользователей
AND AVG(age) > 25; -- И средний возраст больше 25
Полное сравнение: WHERE vs HAVING
-- СЦЕНАРИЙ: найти страны, в которых проживает >100 пользователей старше 18 лет
-- ❌ НЕПРАВИЛЬНО: использование HAVING для фильтрации строк
SELECT country, COUNT(*) as user_count
FROM users
GROUP BY country
HAVING age > 18; -- ← Ошибка! HAVING не может применять условия к строкам
-- ✅ ПРАВИЛЬНО: WHERE для фильтрации строк, HAVING для фильтрации групп
SELECT
country,
COUNT(*) as user_count
FROM users
WHERE age > 18 -- ← Сначала фильтруем возраст (строки)
GROUP BY country
HAVING COUNT(*) > 100; -- ← Потом фильтруем количество (группы)
-- Визуально:
--
-- Исходные данные (users table):
-- ┌──────────┬──────┐
-- │ country │ age │
-- ├──────────┼──────┤
-- │ USA │ 25 │
-- │ USA │ 17 │ ← Отфильтруется WHERE
-- │ USA │ 30 │
-- │ Canada │ 20 │
-- │ Canada │ 16 │ ← Отфильтруется WHERE
-- └──────────┴──────┘
--
-- После WHERE (age > 18):
-- ┌──────────┬──────┐
-- │ country │ age │
-- ├──────────┼──────┤
-- │ USA │ 25 │
-- │ USA │ 30 │
-- │ Canada │ 20 │
-- └──────────┴──────┘
--
-- После GROUP BY:
-- ┌──────────┬─────────────┐
-- │ country │ COUNT(*) │
-- ├──────────┼─────────────┤
-- │ USA │ 2 │ ← Отфильтруется HAVING (2 < 100)
-- │ Canada │ 1 │ ← Отфильтруется HAVING (1 < 100)
-- └──────────┴─────────────┘
--
-- Результат HAVING COUNT(*) > 100: пусто
Производительность: WHERE vs HAVING
-- WHERE БЫСТРЕЕ, потому что фильтрует ДО группировки
-- Таблица: 1,000,000 строк пользователей
-- ❌ НЕОПТИМАЛЬНО
SELECT
country,
COUNT(*) as user_count
FROM users -- Берем 1M строк
GROUP BY country -- Группируем 1M строк (~200 групп)
HAVING age > 18; -- Ошибка синтаксиса, но концептуально неправильно
-- ✅ ОПТИМАЛЬНО
SELECT
country,
COUNT(*) as user_count
FROM users -- Берем 1M строк
WHERE age > 18 -- Фильтруем на 900K строк (100K удаляем)
GROUP BY country -- Группируем 900K строк
HAVING COUNT(*) > 100; -- Фильтруем группы (~50 групп остается)
-- Вывод: WHERE значительно быстрее, потому что работает на меньшем наборе данных
Практические примеры
Пример 1: Продажи
-- Найти менеджеров, которые продали больше $10,000 в июле
SELECT
manager_id,
manager_name,
SUM(sale_amount) as total_sales
FROM sales
WHERE
MONTH(sale_date) = 7 -- ← WHERE: фильтруем месяц (строки)
AND sale_amount > 0 -- ← WHERE: фильтруем только положительные
GROUP BY manager_id, manager_name
HAVING SUM(sale_amount) > 10000; -- ← HAVING: сумма по менеджеру >10K
Пример 2: E-commerce
-- Найти категории товаров, в которых среднее значение > $50
-- И исключить товары со скидкой > 50%
SELECT
category,
COUNT(*) as product_count,
AVG(price) as avg_price
FROM products
WHERE discount < 50 -- ← WHERE: исключаем товары со скидкой >50% (строки)
GROUP BY category
HAVING AVG(price) > 50; -- ← HAVING: средняя цена >50 (группы)
Пример 3: Analytics
-- Пользователи, совершившие >5 заказов и их средний размер заказа >$100
SELECT
user_id,
COUNT(*) as order_count,
AVG(order_amount) as avg_order
FROM orders
WHERE order_date >= '2024-01-01' -- ← WHERE: фильтруем по дате (строки)
GROUP BY user_id
HAVING
COUNT(*) > 5 -- ← HAVING: >5 заказов (группы)
AND AVG(order_amount) > 100; -- ← HAVING: средний >$100 (группы)
Очень важное замечание: WHERE не работает с агрегатами
-- ❌ НЕПРАВИЛЬНО: WHERE не может использовать COUNT, SUM, AVG и т.д.
SELECT
country,
COUNT(*) as user_count
FROM users
WHERE COUNT(*) > 100; -- ← Syntax Error!
-- ✅ ПРАВИЛЬНО: используем HAVING
SELECT
country,
COUNT(*) as user_count
FROM users
GROUP BY country
HAVING COUNT(*) > 100; -- ← Правильно
-- Причина: COUNT(*) вычисляется ДО GROUP BY, а WHERE выполняется ДО GROUP BY
-- Поэтому COUNT(*) еще не вычислен в момент выполнения WHERE
Сравнительная таблица
| Аспект | WHERE | HAVING |
|---|---|---|
| Когда выполняется | ДО GROUP BY | ПОСЛЕ GROUP BY |
| Что фильтрует | Строки | Группы |
| Использует агреганты | ❌ Нет | ✅ Да (COUNT, SUM, AVG) |
| Может использовать обычные колонки | ✅ Да | ✅ Да (из SELECT или GROUP BY) |
| Производительность | Быстрее (меньше данных) | Медленнее (после группировки) |
| Примеры условий | age > 18, city = 'NY' | COUNT(*) > 10, SUM(salary) > 100K |
Вывод для собеседования
Ключевые моменты при ответе:
- ORDER OF EXECUTION — WHERE выполняется ДО GROUP BY, HAVING — ПОСЛЕ
- WHERE = строки, HAVING = группы
- HAVING используется с агрегатными функциями (COUNT, SUM, AVG, MIN, MAX)
- WHERE быстрее, потому что уменьшает объем данных до группировки
- Часто используют вместе: WHERE фильтрует строки, HAVING фильтрует группы
Классический ответ на интервью:
WHERE фильтрует строки ДО GROUP BY, а HAVING фильтрует группы ПОСЛЕ GROUP BY.
Поэтому WHERE работает с отдельными строками, а HAVING может использовать
агрегатные функции как COUNT, SUM, AVG.
Если нужно отфильтровать по условию на отдельные строки — используем WHERE.
Если нужно отфильтровать по условию на результат агреганта — используем HAVING.
Вместе это дает: WHERE для строк, GROUP BY для группировки, HAVING для групп.