← Назад к вопросам
SQL: Найти клиентов с более чем 3 покупками за месяц
1.3 Junior🔥 251 комментариев
#SQL и базы данных
Условие
Есть таблица orders с колонками:
- order_id (integer) - идентификатор заказа
- customer_id (integer) - идентификатор клиента
- order_date (date) - дата заказа
- amount (decimal) - сумма заказа
Найдите клиентов, которые сделали больше трёх покупок за последний месяц.
Ожидаемый результат:
Таблица с колонками:
- customer_id
- orders_count (количество заказов)
- total_amount (общая сумма)
Отсортируйте по количеству заказов по убыванию.
Источник: типовая задача на собеседованиях аналитиков
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Решение: Клиенты с более чем 3 покупками за месяц
SQL-запрос
SELECT
customer_id,
COUNT(order_id) as orders_count,
ROUND(SUM(amount), 2) as total_amount
FROM orders
WHERE order_date >= DATE_TRUNC('month', CURRENT_DATE) - INTERVAL '1 month'
AND order_date < DATE_TRUNC('month', CURRENT_DATE) + INTERVAL '1 month'
GROUP BY customer_id
HAVING COUNT(order_id) > 3
ORDER BY orders_count DESC;
Альтернативный вариант (более универсальный)
WITH last_month AS (
SELECT
customer_id,
COUNT(order_id) as orders_count,
SUM(amount) as total_amount
FROM orders
WHERE order_date >= CURRENT_DATE - INTERVAL '30 days'
GROUP BY customer_id
HAVING COUNT(order_id) > 3
)
SELECT
customer_id,
orders_count,
ROUND(total_amount, 2) as total_amount
FROM last_month
ORDER BY orders_count DESC;
Объяснение
Ключевые элементы:
- WHERE order_date >= ... — фильтруем только заказы из последнего месяца
- Есть несколько вариантов:
- `CURRENT_DATE - INTERVAL '30 days'` — последние 30 дней
- `DATE_TRUNC('month', CURRENT_DATE) - INTERVAL '1 month'` — этот месяц прошлого года
- `EXTRACT(YEAR FROM order_date) = EXTRACT(YEAR FROM CURRENT_DATE) AND EXTRACT(MONTH FROM order_date) = EXTRACT(MONTH FROM CURRENT_DATE) - 1` — конкретный месяц
-
GROUP BY customer_id — группируем по клиентам
-
HAVING COUNT(order_id) > 3 — оставляем только клиентов с более чем 3 заказами
- Важно: HAVING (работает с агрегатами), а не WHERE (для строк)
-
COUNT(order_id) — количество заказов
- Можно также использовать
COUNT(*), ноCOUNT(order_id)явнее
- Можно также использовать
-
SUM(amount) — сумма всех заказов
- ROUND(..., 2) — округляем до двух знаков (для денег)
-
ORDER BY orders_count DESC — сортируем по убыванию количества заказов
Пример вывода
| customer_id | orders_count | total_amount |
|---|---|---|
| 105 | 8 | 12450.50 |
| 203 | 7 | 9875.25 |
| 418 | 5 | 6234.00 |
| 702 | 4 | 5000.00 |
Вариант с дополнительной информацией
Если нужна информация о клиентах (имя, email):
SELECT
c.customer_id,
c.customer_name,
c.email,
COUNT(o.order_id) as orders_count,
ROUND(SUM(o.amount), 2) as total_amount,
ROUND(AVG(o.amount), 2) as avg_order_amount
FROM customers c
JOIN orders o ON c.customer_id = o.customer_id
WHERE o.order_date >= CURRENT_DATE - INTERVAL '30 days'
GROUP BY c.customer_id, c.customer_name, c.email
HAVING COUNT(o.order_id) > 3
ORDER BY orders_count DESC;
Когда использовать какой вариант
Первый вариант (простой):
- Когда нужны только базовые метрики
- Быстрее, меньше JOIN-ов
- Для одноразового анализа
Второй вариант (CTE):
- Более читаемо
- Если нужны дополнительные фильтры
- Если результат используется в других запросах
Третий вариант (с информацией о клиентах):
- Нужна полная информация о клиентах
- Для создания списка VIP-клиентов
- Для отправки персонализированного письма
Производительность
Для оптимизации:
- Индекс на (customer_id, order_date) ускорит WHERE и GROUP BY
- Индекс на order_date сам по себе менее эффективен, если нужны оба поля
CREATE INDEX idx_orders_customer_date
ON orders(customer_id, order_date);