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

Что такое HAVING в SQL?

1.0 Junior🔥 181 комментариев
#Базы данных и SQL

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI21 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

# Что такое HAVING в SQL?

HAVING - это ключевое слово в SQL, которое используется для фильтрации результатов после агрегирования (GROUP BY). Это часто вызывает путаницу, поэтому разберу подробно, в чем разница между WHERE и HAVING.

Основная разница: WHERE vs HAVING

WHERE - фильтрует строки ДО агрегирования HAVING - фильтрует строки ПОСЛЕ агрегирования

1. FROM        ← Читаем исходные данные
2. WHERE       ← Фильтруем строки (ДО группировки)
3. GROUP BY    ← Группируем по указанным полям
4. Агрегация   ← Считаем SUM, COUNT, AVG и т.д.
5. HAVING      ← Фильтруем группы (ПОСЛЕ агрегирования)
6. ORDER BY    ← Сортируем результат
7. LIMIT       ← Ограничиваем количество строк

Пример 1: Простое использование HAVING

-- Задача: найти категории с суммой продаж больше 10000
SELECT 
    category,
    SUM(price * quantity) as total_sales
FROM orders
GROUP BY category
HAVING SUM(price * quantity) > 10000;

Почему HAVING, а не WHERE?

  • SUM() - это агрегирующая функция
  • Она работает только ПОСЛЕ GROUP BY
  • WHERE не может работать с агрегирующими функциями

Если попытаемся использовать WHERE:

-- ❌ Это не сработает!
SELECT category, SUM(price * quantity) as total_sales
FROM orders
WHERE SUM(price * quantity) > 10000  -- Ошибка!
GROUP BY category;

-- Ошибка: aggregate functions in WHERE clause

Пример 2: WHERE + HAVING вместе

-- Задача: найти продавцов с более чем 50 заказами в этом году,
-- у которых средний размер заказа больше 100

SELECT 
    salesman_name,
    COUNT(*) as order_count,
    AVG(order_amount) as avg_amount
FROM orders
WHERE YEAR(order_date) = 2024      -- WHERE: фильтруем ДО группировки
GROUP BY salesman_name
HAVING COUNT(*) > 50               -- HAVING: фильтруем ПОСЛЕ группировки
   AND AVG(order_amount) > 100;

Очередность:

  1. WHERE отсеивает заказы, которые не из 2024 года
  2. GROUP BY группирует оставшиеся заказы по продавцам
  3. COUNT и AVG считаются для каждой группы
  4. HAVING отсеивает группы, где COUNT <= 50 или AVG <= 100

Пример 3: Сложные агрегирующие функции

-- Задача: найти отделы, где:
-- - Минимальная зарплата больше 30000
-- - Максимальная зарплата больше 100000
-- - Средняя зарплата больше 50000

SELECT 
    department,
    MIN(salary) as min_salary,
    MAX(salary) as max_salary,
    AVG(salary) as avg_salary,
    COUNT(*) as employee_count
FROM employees
GROUP BY department
HAVING MIN(salary) > 30000
   AND MAX(salary) > 100000
   AND AVG(salary) > 50000;

Каждое условие в HAVING работает с агрегирующими функциями результата.

Пример 4: HAVING с условиями на COUNT

-- Задача: найти товары, которые заказали 10+ раз за последний месяц
SELECT 
    product_name,
    COUNT(*) as order_count,
    SUM(quantity) as total_quantity
FROM order_items
JOIN orders ON order_items.order_id = orders.id
WHERE orders.created_at >= DATE_SUB(NOW(), INTERVAL 1 MONTH)
GROUP BY product_name
HAVING COUNT(*) >= 10              -- Только товары, заказанные 10+ раз
ORDER BY order_count DESC;

Пример 5: PHP + Laravel с HAVING

// Laravel Eloquent
$products = Product::select('category')
    ->selectRaw('COUNT(*) as product_count')
    ->selectRaw('AVG(price) as avg_price')
    ->selectRaw('SUM(stock) as total_stock')
    ->groupBy('category')
    ->having('product_count', '>', 5)        // HAVING
    ->having('avg_price', '<', 100)          // AND
    ->having('total_stock', '>=', 1000)      // AND
    ->get();

// Сгенерированный SQL:
// SELECT category, COUNT(*) as product_count, AVG(price) as avg_price, SUM(stock) as total_stock
// FROM products
// GROUP BY category
// HAVING product_count > 5 AND avg_price < 100 AND total_stock >= 1000

Пример 6: Raw HAVING с сложной логикой

// Когда нужна сложная логика
$data = DB::table('orders')
    ->select('user_id')
    ->selectRaw('COUNT(*) as order_count')
    ->selectRaw('SUM(total) as total_spent')
    ->groupBy('user_id')
    ->havingRaw('SUM(total) > ? AND COUNT(*) > ?', [5000, 10])
    ->get();

// SQL:
// SELECT user_id, COUNT(*) as order_count, SUM(total) as total_spent
// FROM orders
// GROUP BY user_id
// HAVING SUM(total) > 5000 AND COUNT(*) > 10

Пример 7: HAVING с внешними функциями

-- PostgreSQL пример: найти города, где средняя зарплата больше средней зарплаты по стране
SELECT 
    city,
    AVG(salary) as city_avg_salary
FROM employees
GROUP BY city
HAVING AVG(salary) > (SELECT AVG(salary) FROM employees)
ORDER BY city_avg_salary DESC;

Пример 8: Когда HAVING не нужен

-- ❌ Неправильно - HAVING не требуется
SELECT user_id, name
FROM users
GROUP BY user_id
HAVING user_id > 100;  -- Это можно в WHERE!

-- ✅ Правильно
SELECT user_id, name
FROM users
WHERE user_id > 100
GROUP BY user_id;

-- ✅ Или без GROUP BY вообще
SELECT user_id, name
FROM users
WHERE user_id > 100;

Практические рекомендации

1. Используй WHERE для фильтрации ИСХОДНЫХ данных

-- Сначала отсеиваем нужные строки
WHERE status = 'active'
  AND created_at > '2024-01-01'
  AND country = 'US'

2. Используй HAVING для фильтрации ГРУПП

-- Потом отсеиваем ненужные группы
HAVING COUNT(*) > 10
  AND SUM(amount) > 1000
  AND AVG(rating) >= 4.0

3. Помни об производительности

-- ❌ Плохо: WHERE считает всё, потом HAVING отсеивает
SELECT user_id, COUNT(*) as orders
FROM orders
WHERE status != 'cancelled'  -- Отсеиваем мусор в WHERE
GROUP BY user_id
HAVING COUNT(*) > 5;

-- ✅ Хорошо: WHERE уменьшает объем ДО группировки
SELECT user_id, COUNT(*) as orders
FROM orders
WHERE status = 'completed'   -- Фильтруем ДО GROUP BY
GROUP BY user_id
HAVING COUNT(*) > 5;

Чек-лист использования HAVING

✅ Используй HAVING когда:
   - Нужно фильтровать результаты агрегирующих функций (COUNT, SUM, AVG, MIN, MAX)
   - Нужно отсеивать целые ГРУППЫ, а не отдельные строки
   - Условие зависит от результата GROUP BY

❌ Не используй HAVING когда:
   - Фильтруешь обычные колонки (используй WHERE)
   - Нет GROUP BY в запросе (используй WHERE)
   - Условие не зависит от агрегирования

Итог

HAVING - это WHERE для результатов агрегирования. Она позволяет фильтровать группы на основе агрегирующих функций. Помни:

  1. WHERE работает на исходных данных
  2. HAVING работает на сгруппированных и агрегированных данных
  3. Используй оба вместе для мощной фильтрации
  4. Помни об производительности - лучше фильтровать в WHERE, чем в HAVING