← Назад к вопросам
SQL запрос с GROUP BY и HAVING
2.2 Middle🔥 121 комментариев
#Базы данных и SQL
Условие
Дана таблица orders с колонками: id, customer_id, amount, order_date. Напишите SQL запрос, который находит всех клиентов, сделавших более 5 заказов на общую сумму более 1000.
Ожидаемый результат
customer_id, количество заказов, общая сумма.
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Решение: SQL GROUP BY и HAVING
Описание задачи
Нужно найти клиентов, которые:
- Сделали более 5 заказов (count > 5)
- На общую сумму более 1000 (sum > 1000)
Это классическая задача агрегирования данных в SQL. Используем GROUP BY для группировки по customer_id и HAVING для фильтрации агрегатных функций.
Базовое решение
SELECT
customer_id,
COUNT(*) AS order_count,
SUM(amount) AS total_amount
FROM orders
GROUP BY customer_id
HAVING COUNT(*) > 5 AND SUM(amount) > 1000
ORDER BY total_amount DESC;
Объяснение:
GROUP BY customer_id— группируем заказы по каждому клиентуCOUNT(*)— считаем количество заказов в группеSUM(amount)— суммируем сумму заказов в группеHAVING COUNT(*) > 5 AND SUM(amount) > 1000— фильтруем группыORDER BY total_amount DESC— сортируем по общей сумме (опционально)
Различие между WHERE и HAVING
WHERE: фильтрует строки ПЕРЕД группировкой HAVING: фильтрует ГРУППЫ ПОСЛЕ агрегирования
-- НЕПРАВИЛЬНО: WHERE не работает с COUNT
SELECT customer_id, COUNT(*) as cnt
FROM orders
WHERE COUNT(*) > 5 -- ❌ ОШИБКА
GROUP BY customer_id;
-- ПРАВИЛЬНО: используем HAVING
SELECT customer_id, COUNT(*) as cnt
FROM orders
GROUP BY customer_id
HAVING COUNT(*) > 5; -- ✓ Правильно
Оптимизированное решение
SELECT
customer_id,
COUNT(*) AS order_count,
SUM(amount) AS total_amount
FROM orders
WHERE amount > 0 -- Фильтруем невалидные суммы
GROUP BY customer_id
HAVING COUNT(*) > 5 AND SUM(amount) > 1000
ORDER BY total_amount DESC
LIMIT 100; -- Ограничиваем результаты
Полное решение с дополнительной информацией
SELECT
c.customer_id,
c.customer_name,
COUNT(o.id) AS order_count,
SUM(o.amount) AS total_amount,
AVG(o.amount) AS avg_amount,
MIN(o.order_date) AS first_order,
MAX(o.order_date) AS last_order
FROM customers c
INNER JOIN orders o ON c.id = o.customer_id
GROUP BY c.customer_id, c.customer_name
HAVING COUNT(o.id) > 5 AND SUM(o.amount) > 1000
ORDER BY total_amount DESC;
Альтернатива с подзапросом
SELECT
customer_id,
order_count,
total_amount
FROM (
SELECT
customer_id,
COUNT(*) AS order_count,
SUM(amount) AS total_amount
FROM orders
GROUP BY customer_id
) aggregated
WHERE order_count > 5 AND total_amount > 1000
ORDER BY total_amount DESC;
Шаг за шагом выполнение
Входные данные:
orders таблица:
id | customer_id | amount | order_date
1 | 100 | 150 | 2024-01-01
2 | 100 | 200 | 2024-01-02
3 | 100 | 180 | 2024-01-03
4 | 100 | 220 | 2024-01-04
5 | 100 | 190 | 2024-01-05
6 | 100 | 160 | 2024-01-06
7 | 100 | 175 | 2024-01-07
8 | 200 | 100 | 2024-01-01
...
Шаг 1: GROUP BY создаёт группы
customer_id | orders
100 | [150, 200, 180, 220, 190, 160, 175, ...]
200 | [100, ...]
...
Шаг 2: Агрегирование
customer_id | COUNT(*) | SUM(amount)
100 | 8 | 1435
200 | 2 | 150
...
Шаг 3: HAVING фильтрация
Отбираем только где COUNT(*) > 5 И SUM > 1000:
customer_id | COUNT(*) | SUM(amount)
100 | 8 | 1435
...
Типичные ошибки
- Забыли HAVING: используешь WHERE вместо HAVING
-- ❌ НЕПРАВИЛЬНО
SELECT customer_id, COUNT(*) cnt
FROM orders
WHERE COUNT(*) > 5 -- Ошибка!
GROUP BY customer_id;
- Не указали GROUP BY: агрегирование без группировки
-- ❌ НЕПРАВИЛЬНО
SELECT customer_id, COUNT(*)
FROM orders; -- Должен быть GROUP BY!
- Неправильный синтаксис: используешь колонки вне GROUP BY
-- ❌ НЕПРАВИЛЬНО (в PostgreSQL)
SELECT customer_id, order_date, COUNT(*)
FROM orders
GROUP BY customer_id; -- order_date не в GROUP BY!
Оптимизация для больших таблиц
-- Индекс для быстрого поиска
CREATE INDEX idx_orders_customer ON orders(customer_id, amount);
-- Для очень больших таблиц — партиционирование
CREATE TABLE orders (
id INT,
customer_id INT,
amount DECIMAL,
order_date DATE
) PARTITION BY RANGE YEAR(order_date) (
PARTITION p2024 VALUES LESS THAN (2025),
PARTITION p2025 VALUES LESS THAN (2026)
);
Практическое применение в QA Automation
- Анализ поведения пользователей: найти VIP клиентов для тестирования
- Валидация бизнес-логики: проверить корректность подсчётов
- Поиск аномалий: найти клиентов с необычным поведением покупок
- Загрузочное тестирование: готовить тестовые данные с определёнными критериями
- Регрессионное тестирование: проверить что агрегирование работает правильно
Запрос для проверки в тестах
-- Проверим результат
SELECT * FROM (
SELECT
customer_id,
COUNT(*) AS order_count,
SUM(amount) AS total_amount
FROM orders
GROUP BY customer_id
HAVING COUNT(*) > 5 AND SUM(amount) > 1000
) result
WHERE order_count > 5 -- Двойная проверка
AND total_amount > 1000;