GROUP BY: ein Bericht nach Gruppen
Kurz gesagt: GROUP BY legt Zeilen mit demselben Wert in eine Gruppe, damit COUNT, SUM oder AVG für jede Gruppe einzeln berechnet werden.
Problem
Das Dashboard des Lagers
Das Lager will ein Dashboard: wie viele Bestellungen neu sind, wie viele bezahlt, wie viele verschickt. Mit COUNT(*) und WHERE status = 'new' bräuchtest du fünf Abfragen, eine für jeden Status. Und wenn morgen ein sechster Status auftaucht, zeigt der Bericht ihn nicht.
Du brauchst eine Abfrage, die die Status selbst findet und jeden zählt.
Die Idee
GROUP BY
GROUP BY Spalte legt die Zeilen mit demselben Wert in der Spalte in eine Gruppe. Die Aggregatfunktion wird für jede Gruppe einzeln berechnet:
60 Bestellungen, fünf Gruppen, fünf Zeilen. Jede Gruppe ergibt eine Zeile.
Die Kunden nach Stadt:
Boris hat keine Stadt und steht in einer eigenen Gruppe mit city = NULL. Für GROUP BY sind wie für DISTINCT alle NULL eine Gruppe.
Wie aus einer Gruppe eine Zeile wird, sieht man an wenigen Zeilen leichter. Die letzten drei Bestellungen sind sechs Zeilen in order_items:
SELECT order_id, COUNT(*) AS lines, ROUND(SUM(quantity * unit_price), 2) AS totalFROM order_itemsWHERE order_id >= 58GROUP BY order_id;Schritt für Schritt, in Worten
- 6 Zeilen aus order_items: die Produkte in den Bestellungen 58, 59 und 60.
- GROUP BY order_id: Jede Zeile kommt in die Gruppe ihrer order_id.
- 3 verschiedene order_id, 3 Gruppen. In jeder sind 2 Zeilen.
- Die Gruppe von 58 wird eine Zeile: COUNT(*) ist 2, und SUM(quantity * unit_price) ist 1 × 1,20 + 4 × 1,90 = 8,80 €.
- Die Gruppe von 59: 2 Zeilen, 3 × 2,50 + 2 × 3,20 = 13,90 €.
- Die Gruppe von 60: 2 Zeilen, 2 × 1,20 + 4 × 2,90 = 14,00 €.
- 6 Zeilen gingen hinein, 3 kamen heraus: eine für jede Gruppe.
Die Abfrage aus der Szene. ROUND(…, 2) rundet die Summe auf den Cent:
Probier es aus
Bestellungen pro Kunde
Wie viele Bestellungen hat jeder Kunde aufgegeben? Zeige customer_id und die Anzahl.
Lösung
SELECT customer_id, COUNT(*)
FROM orders
GROUP BY customer_id;SELECT customer_id, COUNT(*) FROM orders GROUP BY customer_id. 18 Zeilen: Zwei der zwanzig Kunden haben keine Bestellungen, sie fehlen also. Kunde 1 hat 12 Bestellungen, die meisten von allen.
Die Idee
Der Wert jeder Bestellung
In order_items belegt eine Bestellung mehrere Zeilen, eine für jedes Produkt. Eine Gruppe nach order_id bringt die Positionen einer Bestellung zusammen:
Jetzt ist jede Bestellung eine Zeile: wie viele Produkte sie hat und wie viel sie wert ist.
Eine Gruppe kann auch nach zwei Spalten gebildet werden. Dann enthält eine Gruppe die Zeilen, in denen beide Werte gleich sind. Die Bestellungen von Kunde 1 nach Status:
Eine Gruppe kann auch nach einem berechneten Wert gebildet werden. strftime('%Y-%m', ordered_at) nimmt Jahr und Monat aus dem Datum, und die Bestellungen werden nach Monat gruppiert:
Sieh es dir an
Die Spalte, die nicht in der Gruppe steht
Die Bestellungen pro Kunde, und daneben der Status. Sieht vernünftig aus:
Die Bestellungen von Kunde 8 zeigen, was los ist:
Drei Bestellungen, drei verschiedene Status. Die Gruppe ist eine Zeile, und sie hat Platz für einen Wert. SQLite sagt nichts und nimmt den Status aus einer der Zeilen. Aus welcher, ist nicht versprochen.
Die Regel: Jede Spalte im SELECT steht entweder in GROUP BY oder in einem Aggregat (COUNT, SUM, MIN …). PostgreSQL und SQL Server melden für so eine Abfrage einen Fehler, und MySQL mit seinen Standardeinstellungen seit Version 5.7 ebenso. SQLite akzeptiert sie und zeigt etwas, das wie eine Antwort aussieht.
Wenn du die Status willst, gruppiere nach beiden Spalten: GROUP BY customer_id, status.
Genauer, für Neugierige
SQLite hat eine Eigenheit, die in der Dokumentation steht: Wenn die Abfrage genau ein MIN oder MAX hat, kommt die freie Spalte aus der Zeile mit dem kleinsten oder größten Wert. SELECT customer_id, MAX(ordered_at), id FROM orders GROUP BY customer_id liefert die Nummer der jüngsten Bestellung. Das gibt es nur in SQLite. Andere Datenbanken lehnen es ab, und im Modul über Unterabfragen sehen wir, wie es überall geht.
Probier es aus
Die Bestseller
Welche fünf Produkte haben sich am häufigsten verkauft? Zeige product_id und die Stückzahl in einer Spalte namens sold, den Bestseller zuerst.
Lösung
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, dann ORDER BY sold DESC LIMIT 5. Vorn liegt Produkt 13 mit 31 Stück: die Gurken, die ausverkauft sind. Vielleicht ist es Zeit für eine neue Lieferung.
Übung
Zwei Berichte
1. Wie viele Produkte und wie viele Stück auf Lager gibt es in jeder Kategorie?
Zeige category_id, die Anzahl der Produkte und die Summe ihres Lagerbestands.
Lösung
SELECT category_id, COUNT(*), SUM(stock)
FROM products
GROUP BY category_id;SELECT category_id, COUNT(*), SUM(stock) FROM products GROUP BY category_id. Sieben Gruppen: sechs Kategorien und NULL für die Geschenkkarte. Ihre 999 Karten sind keine Ware im Regal, deshalb würden wir sie in einem echten Bericht mit WHERE category_id IS NOT NULL weglassen.
2. Die Buchhaltung will die Anzahl der Bestellungen für jeden Monat 2026, nach Monat sortiert.
Zeige den Monat (Jahr-Monat) und die Anzahl der Bestellungen für jeden Monat 2026, in der Reihenfolge der Monate.
Lösung
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' behält die Bestellungen von 2026, GROUP BY strftime('%Y-%m', ordered_at) teilt sie nach Monat, und ORDER BY month sortiert sie. Zehn Monate, von Januar bis Oktober. Warum ORDER BY den Alias month benutzen darf, sehen wir in der letzten Lektion des Moduls.
Test
Teste dich
Verwandte Lektionen
Abschnitt „Verwandte Lektionen“- Baut auf: ORDER BY, LIMIT und DISTINCT · Rechnen in einer Abfrage · Datumsfunktionen · Aggregatfunktionen: COUNT, SUM, AVG
- Geht weiter in: HAVING: Bedingung für Gruppen · Die Reihenfolge der Ausführung einer Abfrage · LEFT JOIN: auch Zeilen ohne Partner · Viele Tabellen und eine Tabelle mit sich selbst verbunden
- Aufgaben zum Thema: Zusammenfassen