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

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

2.2 Middle🔥 231 комментариев
#Базы данных и SQL

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

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

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

HAVING в SQL

HAVING — это SQL клауза, которая фильтрует результаты после GROUP BY агрегирования. В отличие от WHERE, которая фильтрует отдельные строки до агрегирования, HAVING работает с результатами агрегирующих функций (COUNT, SUM, AVG и т.д.).

Различие между WHERE и HAVING

-- WHERE: фильтрует ОТДЕЛЬНЫЕ СТРОКИ до группировки
SELECT 
    category,
    COUNT(*) as total_products,
    AVG(price) as avg_price
FROM products
WHERE price > 100              -- ✓ Фильтрует отдельные строки ДО GROUP BY
GROUP BY category;

-- HAVING: фильтрует РЕЗУЛЬТАТЫ АГРЕГИРОВАНИЯ после группировки
SELECT 
    category,
    COUNT(*) as total_products,
    AVG(price) as avg_price
FROM products
GROUP BY category
HAVING COUNT(*) > 10            -- ✓ Фильтрует результаты ПОСЛЕ GROUP BY
AND AVG(price) > 50;

Порядок выполнения SQL запроса

1. FROM      → выбрать таблицу
2. WHERE     → фильтр отдельных строк
3. GROUP BY  → сгруппировать по значениям
4. HAVING    → фильтр групп (после агрегирования)
5. SELECT    → выбрать нужные колонки/выражения
6. ORDER BY  → отсортировать результат

Пример 1: Подсчёт заказов по клиентам

-- Найти клиентов, которые сделали более 5 заказов
SELECT 
    customer_id,
    customer_name,
    COUNT(order_id) as total_orders,
    SUM(order_amount) as total_spent
FROM orders
GROUP BY customer_id, customer_name
HAVING COUNT(order_id) > 5;  -- Фильтруем группы по количеству заказов

Что происходит:

  1. Все заказы группируются по customer_id
  2. Для каждой группы подсчитываются заказы
  3. HAVING отфильтровывает только группы с COUNT(order_id) > 5

Пример 2: HAVING с AVG (средним значением)

-- Найти категории товаров с средней ценой выше $1000
SELECT 
    category,
    COUNT(*) as product_count,
    AVG(price) as avg_price,
    MAX(price) as max_price
FROM products
GROUP BY category
HAVING AVG(price) > 1000
ORDER BY avg_price DESC;

Пример 3: HAVING с SUM

-- Найти отделы, где общая зарплата всех сотрудников превышает $500,000
SELECT 
    department,
    COUNT(employee_id) as num_employees,
    SUM(salary) as total_salary,
    AVG(salary) as avg_salary
FROM employees
GROUP BY department
HAVING SUM(salary) > 500000
ORDER BY total_salary DESC;

Пример 4: Комбинация WHERE и HAVING

-- WHERE фильтрует строки, HAVING фильтрует группы
SELECT 
    category,
    COUNT(*) as total_products,
    AVG(price) as avg_price
FROM products
WHERE price > 50           -- ДО группировки: только товары с price > 50
GROUP BY category
HAVING COUNT(*) >= 10      -- ПОСЛЕ группировки: только категории с 10+ товарами
AND AVG(price) < 500;

Порядок фильтрации:

1. WHERE price > 50        → отсеять дешёвые товары
2. GROUP BY category       → сгруппировать оставшиеся товары
3. HAVING COUNT(*) >= 10   → фильтровать группы (только с 10+ товарами)

Пример 5: HAVING с условиями на несколько агрегаций

-- Найти города с more than 1000 покупателей и average order > $100
SELECT 
    city,
    COUNT(DISTINCT customer_id) as customer_count,
    COUNT(order_id) as total_orders,
    AVG(order_amount) as avg_order_amount,
    SUM(order_amount) as total_revenue
FROM customer_orders
GROUP BY city
HAVING COUNT(DISTINCT customer_id) > 1000
AND AVG(order_amount) > 100
AND SUM(order_amount) > 50000;

Пример 6: HAVING с логическими операторами

-- Найти продукты, которые либо часто покупают, либо приносят много прибыли
SELECT 
    product_id,
    product_name,
    COUNT(sale_id) as times_sold,
    SUM(profit) as total_profit
FROM sales
GROUP BY product_id, product_name
HAVING COUNT(sale_id) > 100           -- ИЛИ
OR SUM(profit) > 10000;               -- Одно из условий

Пример 7: HAVING с CASE

-- Найти месяцы, где более 50% продаж были премиум-класса
SELECT 
    DATE_TRUNC(month, sale_date) as month,
    COUNT(*) as total_sales,
    SUM(CASE WHEN product_type = premium THEN 1 ELSE 0 END) as premium_sales
FROM sales
GROUP BY DATE_TRUNC(month, sale_date)
HAVING SUM(CASE WHEN product_type = premium THEN 1 ELSE 0 END) 
       > COUNT(*) * 0.5;  -- Более 50%

Использование в Java с ORM (Hibernate/JPA)

// Native SQL
List<Object[]> results = entityManager.createNativeQuery(
    "SELECT category, COUNT(*) as count, AVG(price) as avg_price " +
    "FROM products " +
    "GROUP BY category " +
    "HAVING COUNT(*) > :minCount " +
    "AND AVG(price) > :minPrice"
)
.setParameter("minCount", 10)
.setParameter("minPrice", 50.0)
.getResultList();

// JPA Criteria API
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Object[]> query = cb.createQuery(Object[].class);
Root<Product> product = query.from(Product.class);

query.multiselect(
    product.get("category"),
    cb.count(product),
    cb.avg(product.get("price"))
)
.groupBy(product.get("category"))
.having(
    cb.and(
        cb.gt(cb.count(product), 10L),
        cb.gt(cb.avg(product.get("price")), 50.0)
    )
);

List<Object[]> results = entityManager.createQuery(query).getResultList();

HAVING vs WHERE — Сравнение

ХарактеристикаWHEREHAVING
Когда применяетсяДО GROUP BYПОСЛЕ GROUP BY
ФильтруетОтдельные строкиГруппы строк
Может использоватьКолонки таблицыАгрегирующие функции
ПримерWHERE price > 100HAVING COUNT(*) > 5
ПроизводительностьВыше (фильтр раньше)Ниже (фильтр после агрегации)

Стратегия оптимизации

-- ❌ Неэффективно: WHERE фильтрует после COUNT
SELECT category, COUNT(*) as cnt
FROM products
GROUP BY category
HAVING COUNT(*) > 10;  -- Считает всё, потом фильтрует

-- ✓ Эффективнее: WHERE фильтрует до GROUP BY
SELECT category, COUNT(*) as cnt
FROM products
WHERE status = active  -- Исключает строки ДО GROUP BY
GROUP BY category
HAVING COUNT(*) > 10;

Частые ошибки

Ошибка 1: Использование WHERE для агрегирования

-- ❌ Ошибка: нельзя использовать COUNT в WHERE
SELECT category, COUNT(*) as cnt
FROM products
WHERE COUNT(*) > 10;  -- Ошибка SQL!

-- ✓ Правильно:
SELECT category, COUNT(*) as cnt
FROM products
GROUP BY category
HAVING COUNT(*) > 10;

Ошибка 2: Забыли GROUP BY

-- ❌ Ошибка: HAVING без GROUP BY
SELECT product_id, COUNT(*) as cnt
FROM sales
HAVING cnt > 10;  -- Непредсказуемый результат

-- ✓ Правильно:
SELECT product_id, COUNT(*) as cnt
FROM sales
GROUP BY product_id
HAVING COUNT(*) > 10;

Ошибка 3: Неправильное использование WHERE с GROUP BY

-- ❌ Неправильно: не все выбранные колонки в GROUP BY
SELECT product_id, product_name, COUNT(*) as cnt
FROM products
GROUP BY product_id;  -- product_name не в GROUP BY!

-- ✓ Правильно:
SELECT product_id, product_name, COUNT(*) as cnt
FROM products
GROUP BY product_id, product_name;

Практический пример: Аналитика продаж

-- Найти топ категории по выручке в последний квартал
-- с минимум 100 продажами и average order value > $50

SELECT 
    c.category_name,
    COUNT(s.sale_id) as num_sales,
    SUM(s.amount) as total_revenue,
    AVG(s.amount) as avg_sale_amount,
    MIN(s.amount) as min_sale,
    MAX(s.amount) as max_sale
FROM sales s
JOIN products p ON s.product_id = p.product_id
JOIN categories c ON p.category_id = c.category_id
WHERE s.sale_date >= DATE_SUB(NOW(), INTERVAL 3 MONTH)  -- Последний квартал
GROUP BY c.category_id, c.category_name
HAVING COUNT(s.sale_id) >= 100
AND AVG(s.amount) > 50
ORDER BY total_revenue DESC
LIMIT 10;

HAVING — это неотъемлемая часть SQL для работы с агрегированными данными. Понимание различия между WHERE и HAVING критично для написания эффективных запросов.