Условие
Дано две таблицы: 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;Разбор по шагам
- JOIN — соединяем таблицы по
CustomerID. - GROUP BY — группируем по клиенту.
- COUNT — считаем заказы внутри группы.
- 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.
Подводные камни
COUNT(o.OrderID)vsCOUNT(*)— приINNER JOINэто эквивалентно. ПриLEFT JOINCOUNT(o.OrderID)будет 0 для клиентов без заказов, аCOUNT(*)— 1 (строка с NULL'ами всё равно считается).- Клиенты без заказов —
INNER JOINих выкинет. Если нужны и они —LEFT JOIN+HAVING COUNT(o.OrderID) > 5. GROUP BYпо имени, дубликаты — упомянуто выше.> 5vs>= 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;