Комментарии (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 используется:
- Для фильтра групп — COUNT, SUM, AVG, MIN, MAX на группах
- После GROUP BY — всегда после группировки
- В аналитике — отчёты с условиями на агрегаты
- Вместе с WHERE — WHERE для строк, HAVING для групп
Правило:
WHERE — фильтруем ИНДИВИДУАЛЬНЫЕ строки ДО GROUP BY
HAVING — фильтруем ГРУППЫ ПОСЛЕ GROUP BY
Это один из самых важных SQL концептов для аналитики и отчётности.