GROUP BY: отчет по групи
Накратко: GROUP BY събира редовете с еднаква стойност в група, за да сметне брой, сума или средна за всяка група поотделно.
Проблем
Таблото на склада
Складът иска табло: колко поръчки са нови, колко платени, колко изпратени. С COUNT(*) и WHERE status = 'new' ще ти трябват пет заявки, по една за всяко състояние. А ако утре се появи шесто състояние, отчетът няма да го покаже.
Трябва една заявка, която сама намира състоянията и брои всяко.
Идеята
GROUP BY
GROUP BY колона слага в една група редовете с еднаква стойност в колоната. Обобщаващата функция се смята за всяка група поотделно:
60 поръчки, пет групи, пет реда. Всяка група дава един ред.
Клиентите по град:
Борис е без град и е в отделна група с city = NULL. За GROUP BY, както и за DISTINCT, всички NULL-и са една група.
Как една група става един ред, се вижда на малко редове. Последните три поръчки са шест реда в order_items:
SELECT order_id, COUNT(*) AS lines, ROUND(SUM(quantity * unit_price), 2) AS totalFROM order_itemsWHERE order_id >= 58GROUP BY order_id;Стъпка по стъпка, с думи
- 6 реда от order_items: продуктите в поръчки 58, 59 и 60.
- GROUP BY order_id: всеки ред отива в групата на своя order_id.
- 3 различни order_id, 3 групи. Във всяка има по 2 реда.
- Групата на 58 става един ред: COUNT(*) е 2, а SUM(quantity * unit_price) е 1 × 1,20 + 4 × 1,90 = 8,80 €.
- Групата на 59: 2 реда, 3 × 2,50 + 2 × 3,20 = 13,90 €.
- Групата на 60: 2 реда, 2 × 1,20 + 4 × 2,90 = 14,00 €.
- 6 реда влязоха, 3 излязоха: по един за всяка група.
Заявката от сцената. ROUND(…, 2) закръгля сбора до цент:
Пробвай
Поръчки на клиент
Колко поръчки е направил всеки клиент? Покажи customer_id и броя.
Решение
SELECT customer_id, COUNT(*)
FROM orders
GROUP BY customer_id;SELECT customer_id, COUNT(*) FROM orders GROUP BY customer_id. 18 реда: двама от двайсетте клиенти нямат поръчки и затова ги няма. Клиент 1 е с 12 поръчки, най-много от всички.
Идеята
Стойността на всяка поръчка
В order_items поръчката е на няколко реда, по един за всеки продукт. Групата по order_id събира редовете на една поръчка:
Сега всяка поръчка е един ред: колко продукта има и за колко пари е.
Групата може да е и по две колони. Тогава в една група са редовете, в които и двете стойности са еднакви. Поръчките на клиент 1 по състояние:
Групата може да е и по изчислена стойност. strftime('%Y-%m', ordered_at) взима годината и месеца от датата, и поръчките се групират по месец:
Виж го
Колоната, която не е в групата
Поръчките по клиент, а до тях и състоянието. Изглежда разумно:
Поръчките на клиент 8 показват какво става:
Три поръчки, три различни състояния. Групата е един ред, а в него има място за една стойност. SQLite не казва нищо и взима състоянието от един от редовете. Кой, не е обещано.
Правилото: всяка колона в SELECT е или в GROUP BY, или в обобщение (COUNT, SUM, MIN…). PostgreSQL и SQL Server дават грешка за такава заявка, MySQL също, с настройките си по подразбиране от версия 5.7. SQLite я приема и показва нещо, което изглежда като отговор.
Ако искаш състоянията, групирай по двете колони: GROUP BY customer_id, status.
По-точно, за любопитните
SQLite има една записана в документацията особеност: ако в заявката има точно едно MIN или MAX, свободната колона идва от реда с най-малката или най-голямата стойност. SELECT customer_id, MAX(ordered_at), id FROM orders GROUP BY customer_id дава номера на последната поръчка. Това е само в SQLite. Другите бази го отказват, а в модула за подзаявките ще видим как се прави навсякъде.
Пробвай
Най-продаваните
Кои пет продукта са продадени в най-много бройки? Покажи product_id и бройките в колона sold, най-продаваният пръв.
Решение
SELECT product_id, SUM(quantity) AS sold
FROM order_items
GROUP BY product_id
ORDER BY sold DESC
LIMIT 5;GROUP BY product_id, SUM(quantity) AS sold, после ORDER BY sold DESC LIMIT 5. Пръв е продукт 13 с 31 бройки: краставиците, които са изчерпани. Може би е време за нова доставка.
Упражнение
Две справки
1. Колко продукта и колко бройки на склад има във всяка категория?
Покажи category_id, броя на продуктите и сбора на наличността им.
Решение
SELECT category_id, COUNT(*), SUM(stock)
FROM products
GROUP BY category_id;SELECT category_id, COUNT(*), SUM(stock) FROM products GROUP BY category_id. Седем групи: шест категории и NULL за подаръчната карта. Нейните 999 карти не са стока на рафта, затова в истински отчет бихме я махнали с WHERE category_id IS NOT NULL.
2. Счетоводството иска броя на поръчките за всеки месец от 2026 г., подредени по месец.
Покажи месеца (година-месец) и броя на поръчките за всеки месец от 2026 г., по ред на месеците.
Решение
SELECT strftime('%Y-%m', ordered_at) AS month, COUNT(*)
FROM orders
WHERE ordered_at >= '2026-01-01'
GROUP BY strftime('%Y-%m', ordered_at)
ORDER BY month;WHERE ordered_at >= '2026-01-01' оставя поръчките от 2026 г., GROUP BY strftime('%Y-%m', ordered_at) ги разделя по месеци, а ORDER BY month ги подрежда. Десет месеца, от януари до октомври. Защо ORDER BY може да ползва псевдонима month, ще видим в последния урок от модула.
Тест
Провери се
Свързани уроци
Раздел „Свързани уроци“- Нужно отпреди: ORDER BY, LIMIT и DISTINCT · Сметки в заявката · Функции за дати · Агрегатни функции: COUNT, SUM, AVG
- Продължава в: HAVING: условие за групите · Редът на изпълнение на заявката · LEFT JOIN: и редовете без двойка · Много таблици и таблица със себе си
- Задачи по темата: Обобщаване