Собесов

JOIN + GROUP BY + HAVING: клиенты с более чем 5 заказами

SQLАгрегацияЛёгкаяJunior

Условие

Дано две таблицы: Customers(CustomerID, CustomerName) и Orders(OrderID, CustomerID). Напишите запрос, который вернёт клиентов и количество их заказов, но только для тех, кто сделал более 5 заказов.

Решение

SELECT c.CustomerName,
       COUNT(o.OrderID) AS order_count
FROM   Customers c
INNER JOIN Orders o
       ON c.CustomerID = o.CustomerID
GROUP BY c.CustomerName
HAVING COUNT(o.OrderID) > 5;

Разбор по шагам

  1. JOIN — соединяем таблицы по CustomerID.
  2. GROUP BY — группируем по клиенту.
  3. COUNT — считаем заказы внутри группы.
  4. HAVING — фильтруем группы, где COUNT > 5.

Почему HAVING, а не WHERE

Условие касается агрегата: COUNT(*) > 5 нельзя проверить до GROUP BY (агрегат ещё не посчитан). Поэтому только HAVING.

Группировать по CustomerName или CustomerID?

-- лучше: по уникальному ключу
GROUP BY c.CustomerID, c.CustomerName

Если у двух клиентов одинаковое имя, группировка по CustomerName склеит их. По CustomerID — корректно. В стандартном SQL все не-агрегатные колонки в SELECT должны быть в GROUP BY.

Альтернатива через подзапрос

SELECT c.CustomerName, x.cnt
FROM   Customers c
JOIN (
  SELECT CustomerID, COUNT(*) AS cnt
  FROM   Orders
  GROUP BY CustomerID
  HAVING COUNT(*) > 5
) x ON c.CustomerID = x.CustomerID;

Иногда это быстрее: агрегация на меньшей таблице до JOIN.

Подводные камни

  1. COUNT(o.OrderID) vs COUNT(*) — при INNER JOIN это эквивалентно. При LEFT JOIN COUNT(o.OrderID) будет 0 для клиентов без заказов, а COUNT(*) — 1 (строка с NULL'ами всё равно считается).
  2. Клиенты без заказовINNER JOIN их выкинет. Если нужны и они — LEFT JOIN + HAVING COUNT(o.OrderID) > 5.
  3. GROUP BY по имени, дубликаты — упомянуто выше.
  4. > 5 vs >= 5 — внимательно читайте условие. «Более 5» = > 5 = от 6 заказов.

Эталонный ответ

SELECT c.CustomerName, COUNT(o.OrderID) AS order_count
FROM   Customers c
INNER JOIN Orders o ON c.CustomerID = o.CustomerID
GROUP BY c.CustomerID, c.CustomerName
HAVING COUNT(o.OrderID) > 5;

Хочешь увидеть разбор?

Зарегистрируйся бесплатно — откроется развёрнутое решение этой задачи и ещё 4 на выбор.

Зарегистрироваться и увидеть разбор
Уже есть аккаунт? Войти