← Назад к вопросам

В чем разница между 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

Сравнительная таблица

АспектWHEREHAVING
Когда выполняетсяДО GROUP BYПОСЛЕ GROUP BY
Что фильтруетСтрокиГруппы
Использует агреганты❌ Нет✅ Да (COUNT, SUM, AVG)
Может использовать обычные колонки✅ Да✅ Да (из SELECT или GROUP BY)
ПроизводительностьБыстрее (меньше данных)Медленнее (после группировки)
Примеры условийage > 18, city = 'NY'COUNT(*) > 10, SUM(salary) > 100K

Вывод для собеседования

Ключевые моменты при ответе:

  1. ORDER OF EXECUTION — WHERE выполняется ДО GROUP BY, HAVING — ПОСЛЕ
  2. WHERE = строки, HAVING = группы
  3. HAVING используется с агрегатными функциями (COUNT, SUM, AVG, MIN, MAX)
  4. WHERE быстрее, потому что уменьшает объем данных до группировки
  5. Часто используют вместе: WHERE фильтрует строки, HAVING фильтрует группы

Классический ответ на интервью:

WHERE фильтрует строки ДО GROUP BY, а HAVING фильтрует группы ПОСЛЕ GROUP BY.
Поэтому WHERE работает с отдельными строками, а HAVING может использовать
агрегатные функции как COUNT, SUM, AVG.

Если нужно отфильтровать по условию на отдельные строки — используем WHERE.
Если нужно отфильтровать по условию на результат агреганта — используем HAVING.
Вместе это дает: WHERE для строк, GROUP BY для группировки, HAVING для групп.
В чем разница между HAVING и WHERE в SQL? | PrepBro