Фильтрация групп: HAVING
Когда GROUP BY недостаточно 🔍
Вы уже освоили группировку с GROUP BY — это мощный инструмент для работы с агрегированными данными. Но что делать, когда нужно отфильтровать не отдельные строки, а целые группы? Именно здесь на сцену выходит HAVING — ваш новый лучший друг в SQL.
HAVING — это WHERE для групп. Он работает после группировки и позволяет фильтровать результаты агрегации.
SELECT column1, aggregate_function(column2)
FROM table
GROUP BY column1
HAVING condition;
Основные отличия HAVING от WHERE 🆚
WHEREфильтрует строки до группировкиHAVINGфильтрует группы после группировки- В
HAVINGможно использовать агрегатные функции (SUM,AVG,COUNTи др.)
Реальный пример: анализ продаж 🛒
Представим таблицу orders с данными о заказах:
SELECT customer_id, COUNT(*) as order_count, SUM(amount) as total_spent
FROM orders
GROUP BY customer_id
HAVING SUM(amount) > 1000 AND COUNT(*) >= 5;
Этот запрос найдет клиентов:
- С общей суммой покупок более 1000
- Сделавших 5 и более заказов
Работа с агрегатными функциями 🧮
HAVING особенно полезен при работе с агрегатами:
SELECT department, AVG(salary) as avg_salary
FROM employees
GROUP BY department
HAVING AVG(salary) > (SELECT AVG(salary) FROM employees);
Здесь мы находим отделы, где средняя зарплата превышает среднюю по компании.
Оптимизация производительности ⚡
Помните: сначала фильтруйте строки с WHERE, затем группы с HAVING:
-- Оптимальный вариант:
SELECT product_id, SUM(quantity) as total_sold
FROM order_items
WHERE order_date BETWEEN '2023-01-01' AND '2023-12-31'
GROUP BY product_id
HAVING SUM(quantity) > 100;
-- Менее эффективный вариант:
SELECT product_id, SUM(quantity) as total_sold
FROM order_items
GROUP BY product_id
HAVING SUM(quantity) > 100
AND MIN(order_date) >= '2023-01-01'
AND MAX(order_date) <= '2023-12-31';
Комбинирование условий в HAVING 🧩
Вы можете комбинировать условия с AND, OR и скобками:
SELECT category,
AVG(price) as avg_price,
COUNT(*) as product_count
FROM products
GROUP BY category
HAVING (AVG(price) > 50 OR COUNT(*) > 10)
AND MAX(price) < 1000;
Частые ошибки и как их избежать 🚧
1. Использование алиасов в HAVING:
-- Так нельзя:
SELECT department, AVG(salary) as avg_sal
FROM employees
GROUP BY department
HAVING avg_sal > 50000;
-- Правильно:
HAVING AVG(salary) > 50000;
2. Путаница между WHERE и HAVING:
-- Фильтрация отдельных строк:
WHERE salary > 50000
-- Фильтрация групп:
HAVING AVG(salary) > 50000
Практическое применение: анализ данных 📊
Давайте решим реальную бизнес-задачу: найдем товары, которые хорошо продаются, но имеют низкую маржу:
SELECT p.product_name,
SUM(oi.quantity) as total_units_sold,
SUM(oi.quantity * oi.unit_price) as total_revenue,
SUM(oi.quantity * (oi.unit_price - p.cost)) as total_profit,
(SUM(oi.quantity * (oi.unit_price - p.cost)) / SUM(oi.quantity * oi.unit_price)) * 100 as profit_margin
FROM order_items oi
JOIN products p ON oi.product_id = p.product_id
GROUP BY p.product_name
HAVING SUM(oi.quantity) > 100
AND (SUM(oi.quantity * (oi.unit_price - p.cost)) / SUM(oi.quantity * oi.unit_price)) * 100 < 15
ORDER BY total_units_sold DESC;
Этот запрос поможет выявить популярные товары с низкой рентабельностью для дальнейшего анализа ценовой политики.
Продвинутые техники: вложенные агрегаты 🎯
HAVING можно использовать с вложенными агрегатными функциями:
SELECT department,
AVG(salary) as avg_dept_salary
FROM employees
GROUP BY department
HAVING AVG(salary) > (SELECT AVG(salary) FROM employees);
Этот запрос найдет отделы, где средняя зарплата выше средней по всей компании.