Фильтрация групп: HAVING

Когда GROUP BY недостаточно 🔍

Вы уже освоили группировку с GROUP BY — это мощный инструмент для работы с агрегированными данными. Но что делать, когда нужно отфильтровать не отдельные строки, а целые группы? Именно здесь на сцену выходит HAVING — ваш новый лучший друг в SQL.

HAVING — это WHERE для групп. Он работает после группировки и позволяет фильтровать результаты агрегации.

SELECT column1, aggregate_function(column2)
FROM table
GROUP BY column1
HAVING condition;

Основные отличия HAVING от WHERE 🆚

  1. WHERE фильтрует строки до группировки
  2. HAVING фильтрует группы после группировки
  3. В 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);

Этот запрос найдет отделы, где средняя зарплата выше средней по всей компании.

Скрыть рекламу навсегда

🌱 Индвидидулаьные занятия

Индивидуальные онлайн-занятия по программированию для детей и подростков

Личный подход, без воды, с фокусом на понимание и реальные проекты.

🚀 Записаться на занятие