GROUP BY fasst Zeilen mit gleichem Wert zu Gruppen zusammen, sodass Aggregatfunktionen wie COUNT, SUM und AVG ein Ergebnis pro Gruppe liefern statt eines für die ganze Tabelle. Die Beispiele funktionieren in PostgreSQL, MySQL, SQLite und SQL Server.
So verwendest du GROUP BY
Nenne nach GROUP BY die Spalte, nach der gruppiert werden soll, und packe jede andere ausgewählte Spalte in eine Aggregatfunktion: COUNT, SUM, AVG, MIN oder MAX. Du bekommst eine Zeile pro eindeutigem Wert:
SELECT group_column, AGGREGATE(value_column)
FROM table_name
GROUP BY group_column;
-- Input: scores
-- player | team | points
-- -------+------+-------
-- Ana | Red | 12
-- Ben | Blue | 9
-- Cleo | Red | 7
-- Dev | Blue | 15
-- Eli | Red | 4
SELECT team, SUM(points) AS total_points, COUNT(*) AS players
FROM scores
GROUP BY team;
-- Output:
-- team | total_points | players
-- -----+--------------+--------
-- Red | 23 | 3
-- Blue | 24 | 2
Aus fünf Zeilen werden zwei, eine pro Team, und jedes Aggregat sieht nur die Zeilen seines Teams.
Wichtige Regeln
- Jede ausgewählte Spalte muss in GROUP BY stehen oder in einem Aggregat. Sonst gibt es keinen eindeutigen Wert für die Gruppe, und die Abfrage schlägt fehl:
-- Input: products
-- name | category
-- --------+---------
-- Blender | Kitchen
-- Toaster | Kitchen
-- Pillow | Bedroom
SELECT name, category, COUNT(*) AS product_count
FROM products
GROUP BY category;
-- ERROR: column "products.name" must appear in the GROUP BY clause
-- or be used in an aggregate function
-- (SQLite runs it anyway and takes name from one row in each group)
Behebe das, indem du die Spalte zu GROUP BY hinzufügst oder sie ebenfalls aggregierst, zum Beispiel mit MIN(name).
- NULLs bilden eine Gruppe. Jede Zeile mit NULL in der Gruppierungsspalte landet in derselben Gruppe. Mit COALESCE gibst du ihr eine Bezeichnung:
-- Input: products
-- name | category
-- ------------+-----------
-- Notebook | Stationery
-- Gift Card | NULL
-- Pen Set | Stationery
-- Mystery Box | NULL
-- Stapler | Stationery
SELECT category, COUNT(*) AS product_count
FROM products
GROUP BY category;
-- Output:
-- category | product_count
-- -----------+--------------
-- NULL | 2
-- Stationery | 3
SELECT COALESCE(category, 'Uncategorized') AS category, COUNT(*) AS product_count
FROM products
GROUP BY category;
-- Output:
-- category | product_count
-- --------------+--------------
-- Uncategorized | 2
-- Stationery | 3
-
WHERE filtert Zeilen vor dem Gruppieren, HAVING filtert Gruppen danach. Nur HAVING kann ein Aggregat wie
COUNT(*) > 2prüfen. Alles, was kein Aggregat braucht, gehört in WHERE, damit weniger Zeilen gruppiert werden. - Gruppen kommen in keiner bestimmten Reihenfolge zurück. SQLite und SQL Server listen bei der Abfrage oben zum Beispiel Blue vor Red. Füge ORDER BY hinzu, wenn die Reihenfolge wichtig ist.
Alternative Syntax
Statt einen Ausdruck zu wiederholen, kannst du nach einem Spaltenalias oder nach der Position der Spalte in der SELECT-Liste gruppieren:
-- Input: orders
-- order_id | amount
-- ---------+-------
-- 1 | 35.00
-- 2 | 240.00
-- 3 | 18.50
-- 4 | 120.00
-- 5 | 64.00
SELECT CASE WHEN amount >= 100 THEN 'large' ELSE 'small' END AS order_size,
COUNT(*) AS order_count
FROM orders
GROUP BY order_size -- or GROUP BY 1
ORDER BY order_size;
-- SQL Server allows neither: repeat the CASE expression in GROUP BY
-- Output:
-- order_size | order_count
-- -----------+------------
-- large | 2
-- small | 3
ROLLUP fügt Zwischensummen und eine Gesamtsumme hinzu. Die Zwischensummenzeilen zeigen NULL in den Spalten, die sie zusammenfassen:
-- Input: sales
-- region | category | amount
-- -------+----------+-------
-- East | Books | 100.00
-- East | Toys | 50.00
-- West | Books | 80.00
-- East | Books | 40.00
SELECT region, category, SUM(amount) AS total_sales
FROM sales
GROUP BY ROLLUP(region, category);
-- MySQL: GROUP BY region, category WITH ROLLUP
-- SQLite has no ROLLUP
-- Output:
-- region | category | total_sales
-- -------+----------+------------
-- East | Books | 140.00
-- East | Toys | 50.00
-- East | NULL | 190.00
-- West | Books | 80.00
-- West | NULL | 80.00
-- NULL | NULL | 270.00
Ein GROUP BY ohne Aggregat liefert jeden eindeutigen Wert einmal, genau wie SELECT DISTINCT category FROM products.
Wann solltest du GROUP BY verwenden?
Nutze GROUP BY immer dann, wenn eine Frage „pro“ oder „für jede(n)“ enthält: Bestellungen pro Kunde, Umsatz pro Monat, Mitarbeitende pro Abteilung. Die meisten Anwendungsfälle gehören zu einer dieser vier Kategorien.
Zeilen pro Gruppe zählen
Wie viele Bestellungen welchen Status haben, wie viele Produkte in jeder Kategorie sind, wie viele Leute sich pro Tag angemeldet haben. COUNT(*) zählt jede Zeile der Gruppe, COUNT(column) nur die Zeilen, in denen diese Spalte nicht NULL ist.
Summe, Durchschnitt oder Spanne pro Gruppe
SUM, AVG, MIN und MAX machen aus den Werten jeder Gruppe eine einzige Zahl: Umsatz pro Kunde, durchschnittlicher Bestellwert, das günstigste und teuerste Produkt einer Kategorie. Eine Abfrage kann so viele davon liefern, wie du brauchst.
Ergebnisse nach mehreren Spalten aufschlüsseln
Gib mehrere Spalten nach GROUP BY an, um eine Zeile pro Kombination zu bekommen: Umsatz pro Region pro Monat, Bestand pro Lager pro Produkttyp. Es erscheinen nur Kombinationen, die in den Daten vorkommen.
Nur relevante Gruppen behalten
HAVING filtert die fertigen Gruppen anhand ihrer Aggregate: Abteilungen mit mehr als zehn Leuten, Kunden, die über $1,000 ausgegeben haben, oder Werte, die mehr als einmal vorkommen – so findest du Duplikate.
Praxisbeispiele für GROUP BY
Produkte pro Kategorie zählen, auch leere Kategorien
GROUP BY bildet nur Gruppen für Werte, die existieren. Um auch Kategorien ohne Produkte aufzulisten, starte mit der Tabelle categories und verbinde die Produkte per LEFT JOIN. Zähle eine Spalte aus der verbundenen Tabelle statt *: Eine leere Kategorie bekommt trotzdem eine mit NULLs aufgefüllte Zeile, und COUNT(*) zählt sie mit.
-- Input: categories
-- category_name
-- -------------
-- Audio
-- Cameras
-- Drones
-- Input: products
-- id | name | category
-- ---+------------+---------
-- 1 | Earbuds | Audio
-- 2 | Soundbar | Audio
-- 3 | Action Cam | Cameras
-- Wrong: COUNT(*) counts the NULL row of an empty category
SELECT c.category_name, COUNT(*) AS product_count
FROM categories c
LEFT JOIN products p ON p.category = c.category_name
GROUP BY c.category_name
ORDER BY c.category_name;
-- Output:
-- category_name | product_count
-- --------------+--------------
-- Audio | 2
-- Cameras | 1
-- Drones | 1
-- Right: COUNT(p.id) skips it
SELECT c.category_name, COUNT(p.id) AS product_count
FROM categories c
LEFT JOIN products p ON p.category = c.category_name
GROUP BY c.category_name
ORDER BY c.category_name;
-- Output:
-- category_name | product_count
-- --------------+--------------
-- Audio | 2
-- Cameras | 1
-- Drones | 0
Die drei umsatzstärksten Kategorien finden
Summiere die Verkäufe jeder Kategorie, sortiere nach der Summe und behalte die ersten drei. Books landet auf Platz zwei, obwohl jeder einzelne Verkauf kleiner ist als der eine Verkauf bei Toys:
-- Input: purchases
-- category | amount
-- ---------+-------
-- Books | 45.00
-- Toys | 120.00
-- Garden | 310.00
-- Books | 80.00
-- Music | 25.00
SELECT category, SUM(amount) AS revenue
FROM purchases
GROUP BY category
ORDER BY revenue DESC
LIMIT 3;
-- SQL Server has no LIMIT: write SELECT TOP 3 category, ... instead
-- Output:
-- category | revenue
-- ---------+--------
-- Garden | 310.00
-- Books | 125.00
-- Toys | 120.00
Umsatz pro Region und Monat auswerten
Gruppiere nach der Region und nach dem aus dem Datum extrahierten Monat, sodass jede Region eine Zeile pro Monat bekommt:
-- Input: sales
-- region | sale_date | amount
-- -------+------------+-------
-- East | 2024-01-08 | 120.00
-- West | 2024-01-15 | 80.00
-- East | 2024-01-22 | 95.00
-- East | 2024-02-03 | 60.00
-- West | 2024-02-11 | 150.00
SELECT region, EXTRACT(MONTH FROM sale_date) AS month, SUM(amount) AS total_sales
FROM sales
GROUP BY region, EXTRACT(MONTH FROM sale_date)
ORDER BY region, month;
-- Output:
-- region | month | total_sales
-- -------+-------+------------
-- East | 1 | 215.00
-- East | 2 | 60.00
-- West | 1 | 80.00
-- West | 2 | 150.00
SQL Server und SQLite haben kein EXTRACT. Verwende MONTH(sale_date) in SQL Server und strftime('%m', sale_date) in SQLite. Gruppiere zusätzlich nach dem Jahr, sobald deine Daten mehr als eins umfassen.
Stammkunden in diesem Jahr finden
WHERE behält nur die Bestellungen aus 2024, dann behält HAVING die Kunden mit mindestens zwei davon. Kunde 202 hat insgesamt zwei Bestellungen, aber nur eine in 2024, und fällt deshalb raus:
-- Input: orders
-- order_id | customer_id | order_date
-- ---------+-------------+-----------
-- 1 | 201 | 2023-11-20
-- 2 | 202 | 2023-12-05
-- 3 | 201 | 2024-01-09
-- 4 | 202 | 2024-02-14
-- 5 | 201 | 2024-03-02
SELECT customer_id, COUNT(*) AS orders_2024
FROM orders
WHERE order_date >= '2024-01-01'
GROUP BY customer_id
HAVING COUNT(*) >= 2;
-- Output:
-- customer_id | orders_2024
-- ------------+------------
-- 201 | 2
Weiterführende Artikel zu GROUP BY
In diesem Blog:
- SQL COUNT DISTINCT Guide: eindeutige Werte pro Gruppe zählen
-
SQL Duplikate finden Guide: GROUP BY mit
HAVING COUNT(*) > 1 - SQL ORDER BY Guide: gruppierte Ergebnisse sortieren
- SQL CASE Guide: bedingte Zählungen und Summen in einem Aggregat
- SQL Pivot-Tabellen Guide: gruppierte Zeilen in Spalten umwandeln
- SQL COALESCE Guide: die NULL-Gruppe beschriften
- SQL JOIN Diagramme Guide: der LEFT JOIN, der leere Gruppen behält
Offizielle Dokumentation:
- PostgreSQL: GROUP BY und HAVING
- MySQL: Handling of GROUP BY
- SQLite: SELECT (Aggregatabfragen)
- SQL Server: GROUP BY
Beekeeper Studio ist eine kostenlose & quelloffene Datenbank-GUI
Das beste SQL-Abfrage- & Editor-Tool, das ich je benutzt habe. Es bietet alles, was ich zum Verwalten meiner Datenbank brauche. - ⭐⭐⭐⭐⭐ Mit
Beekeeper Studio ist schnell, intuitiv und einfach zu bedienen. Beekeeper unterstützt jede Menge Datenbanken und läuft hervorragend auf Windows, Mac und Linux.
Was Nutzer über Beekeeper Studio sagen
„Beekeeper Studio hat meinen alten SQL-Workflow komplett ersetzt. Es ist schnell, intuitiv und macht die Arbeit mit Datenbanken wieder zum Vergnügen.“
„Ich habe viele Datenbank-GUIs ausprobiert, aber Beekeeper trifft die perfekte Balance zwischen Funktionsumfang und Einfachheit. Es funktioniert einfach.“