← Назад к вопросам

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;

Объяснение

Ключевые элементы:

  1. 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` — конкретный месяц

  1. GROUP BY customer_id — группируем по клиентам

  2. HAVING COUNT(order_id) > 3 — оставляем только клиентов с более чем 3 заказами

    • Важно: HAVING (работает с агрегатами), а не WHERE (для строк)
  3. COUNT(order_id) — количество заказов

    • Можно также использовать COUNT(*), но COUNT(order_id) явнее
  4. SUM(amount) — сумма всех заказов

    • ROUND(..., 2) — округляем до двух знаков (для денег)
  5. ORDER BY orders_count DESC — сортируем по убыванию количества заказов

Пример вывода

customer_idorders_counttotal_amount
105812450.50
20379875.25
41856234.00
70245000.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);