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

Где используешь HAVING?

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

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

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

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

Где и когда использовать HAVING

HAVING — это SQL clause, который позволяет фильтровать результаты ПОСЛЕ группировки, в отличие от WHERE который фильтрует ДО группировки.

Разница между WHERE и HAVING

WHERE — фильтр на уровне отдельных строк ДО GROUP BY:

SELECT department, COUNT(*) as employee_count
FROM employees
WHERE salary > 50000  -- Отфильтруем сотрудников ДО группировки
GROUP BY department;

HAVING — фильтр на уровне групп ПОСЛЕ GROUP BY:

SELECT department, COUNT(*) as employee_count
FROM employees
GROUP BY department
HAVING COUNT(*) > 5;  -- Только отделы с более чем 5 сотрудниками

Практические примеры использования HAVING

1. Фильтр по агрегирующей функции

-- Найди товары, которые продались более 100 раз
SELECT 
    product_id, 
    product_name,
    COUNT(*) as sales_count,
    SUM(amount) as total_revenue
FROM sales
GROUP BY product_id, product_name
HAVING COUNT(*) > 100;  -- HAVING для фильтра по COUNT

2. Средние значения группы

-- Отделы где средняя зарплата выше 70000
SELECT 
    department_id,
    department_name,
    AVG(salary) as avg_salary,
    COUNT(*) as employee_count
FROM employees
GROUP BY department_id, department_name
HAVING AVG(salary) > 70000;  -- Фильтр по среднему значению

3. Сумма с условием

-- Клиенты, потративших более $10000
SELECT 
    customer_id,
    customer_name,
    SUM(order_amount) as total_spent,
    COUNT(*) as order_count
FROM orders
GROUP BY customer_id, customer_name
HAVING SUM(order_amount) > 10000;  -- Фильтр по сумме

Где используется HAVING

1. Аналитика данных

-- Категории товаров с высоким средним рейтингом
SELECT 
    category,
    COUNT(product_id) as product_count,
    AVG(rating) as avg_rating,
    MIN(rating) as min_rating,
    MAX(rating) as max_rating
FROM products
GROUP BY category
HAVING AVG(rating) >= 4.5  -- Только лучшие категории
ORDER BY avg_rating DESC;

2. Финансовые отчёты

-- Продавцы с доходом выше целевого показателя
SELECT 
    seller_id,
    seller_name,
    SUM(commission) as total_commission,
    COUNT(DISTINCT order_id) as orders_count
FROM sales
WHERE status = 'completed'  -- WHERE для отдельных строк
GROUP BY seller_id, seller_name
HAVING SUM(commission) > 5000  -- HAVING для групп
ORDER BY total_commission DESC;

3. Проверка качества данных

-- Пользователи, которые делали слишком много покупок подряд (могут быть боты)
SELECT 
    user_id,
    COUNT(*) as purchase_count,
    COUNT(DISTINCT DATE(created_at)) as distinct_days
FROM purchases
WHERE created_at > NOW() - INTERVAL 7 DAY  -- За неделю
GROUP BY user_id
HAVING COUNT(*) > 50 AND COUNT(DISTINCT DATE(created_at)) = 1;  -- МНОГО за день

HAVING vs WHERE — примеры кода

-- ПРИМЕР 1: Отделы с количеством сотрудников

-- НЕПРАВИЛЬНО (WHERE не может использовать COUNT)
SELECT department, COUNT(*) as emp_count
FROM employees
WHERE COUNT(*) > 5  -- Ошибка! COUNT используется в WHERE
GROUP BY department;

-- ПРАВИЛЬНО
SELECT department, COUNT(*) as emp_count
FROM employees
GROUP BY department
HAVING COUNT(*) > 5;  -- Правильно!
-- ПРИМЕР 2: Комбинирование WHERE и HAVING

SELECT 
    category,
    COUNT(*) as product_count,
    AVG(price) as avg_price
FROM products
WHERE status = 'active' AND price > 10  -- WHERE — фильтр ДО группировки
GROUP BY category
HAVING AVG(price) < 100;  -- HAVING — фильтр ПОСЛЕ группировки

В Java/Spring Data

На QueryDSL:

QProduct product = QProduct.product;
QOrder order = QOrder.order;

List<Tuple> results = queryFactory
    .select(product.category, product.name, product.count())
    .from(product)
    .where(product.price.gt(100))  // WHERE — фильтр строк
    .groupBy(product.category, product.name)
    .having(product.count().gt(5))  // HAVING — фильтр групп
    .fetch();

На Spring Data JPA:

@Repository
public interface ProductRepository extends JpaRepository<Product, Long> {
    
    // Нельзя напрямую написать HAVING в методе запроса
    // Нужно использовать @Query
    
    @Query("""
        SELECT new map(
            p.category as category,
            COUNT(p) as product_count,
            AVG(p.price) as avg_price
        )
        FROM Product p
        WHERE p.status = 'ACTIVE'
        GROUP BY p.category
        HAVING COUNT(p) > :minCount
        ORDER BY avg_price DESC
    """)
    List<Map<String, Object>> findCategoriesWithMinProducts(
        @Param("minCount") long minCount
    );
}

На нативном SQL:

@Repository
public interface SalesRepository extends JpaRepository<Sale, Long> {
    
    @Query(value = """
        SELECT 
            customer_id,
            customer_name,
            SUM(amount) as total_spent
        FROM sales
        WHERE status = 'COMPLETED'
        GROUP BY customer_id, customer_name
        HAVING SUM(amount) > :minAmount
        ORDER BY total_spent DESC
    """, nativeQuery = true)
    List<Object[]> findTopCustomers(@Param("minAmount") BigDecimal minAmount);
}

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

1. Использование WHERE вместо HAVING

-- ОШИБКА
SELECT category, COUNT(*) as count
FROM products
WHERE COUNT(*) > 5  -- Ошибка! WHERE не может использовать агрегаты
GROUP BY category;

-- ПРАВИЛЬНО
SELECT category, COUNT(*) as count
FROM products
GROUP BY category
HAVING COUNT(*) > 5;

2. Фильтрация по полю вместо агрегата

-- Если нужно фильтровать по полю — используй WHERE
SELECT category, COUNT(*) as count
FROM products
WHERE status = 'ACTIVE'  -- Правильно в WHERE
GROUP BY category
HAVING COUNT(*) > 5;     -- HAVING для агрегатов

3. Забывчивость GROUP BY

-- ОШИБКА
SELECT category, COUNT(*)
FROM products
HAVING COUNT(*) > 5;
-- Ошибка! GROUP BY обязателен перед HAVING

-- ПРАВИЛЬНО
SELECT category, COUNT(*)
FROM products
GROUP BY category
HAVING COUNT(*) > 5;

Производительность

-- ХУД: WHERE после GROUP BY — медленнее
SELECT 
    user_id,
    COUNT(*) as order_count
FROM orders
GROUP BY user_id
HAVING user_id IN (SELECT user_id FROM users WHERE country = 'US');

-- ХОРОШО: WHERE перед GROUP BY — быстрее
SELECT 
    u.user_id,
    COUNT(o.id) as order_count
FROM users u
LEFT JOIN orders o ON u.user_id = o.user_id
WHERE u.country = 'US'
GROUP BY u.user_id
HAVING COUNT(o.id) > 0;

Вывод

HAVING используется:

  1. Для фильтра групп — COUNT, SUM, AVG, MIN, MAX на группах
  2. После GROUP BY — всегда после группировки
  3. В аналитике — отчёты с условиями на агрегаты
  4. Вместе с WHERE — WHERE для строк, HAVING для групп

Правило:

WHERE — фильтруем ИНДИВИДУАЛЬНЫЕ строки ДО GROUP BY
HAVING — фильтруем ГРУППЫ ПОСЛЕ GROUP BY

Это один из самых важных SQL концептов для аналитики и отчётности.

Где используешь HAVING? | PrepBro