MSSQL Aggregatfunktionen verdichten mehrere Zeilen zu einem Ergebnis: Wie viele Bestellungen gab es? Wie hoch ist der Gesamtumsatz? Welcher Betrag ist der kleinste oder größte? In SQL Server erledigen das unter anderem COUNT, SUM, AVG, MIN und MAX. Dieser Beitrag zeigt ihre Unterschiede anhand eines gemeinsamen Beispiels und erklärt, wie du mit GROUP BY und HAVING aussagekräftige Auswertungen erstellst.
Was sind MSSQL Aggregatfunktionen?
Eine Aggregatfunktion berechnet aus einer Menge von Eingabewerten einen Wert. Ohne GROUP BY erhältst du üblicherweise ein Ergebnis für alle passenden Zeilen. Mit GROUP BY entsteht ein Ergebnis pro Gruppe, etwa pro Region. Funktionen mit einem Spaltenausdruck wie SUM(Betrag) oder COUNT(Betrag) ignorieren NULL-Werte; COUNT(*) und COUNT_BIG(*) zählen dagegen alle Zeilen. Das ist bei Zählungen und Durchschnittswerten besonders wichtig. Die Microsoft-Übersicht zu T-SQL-Aggregatfunktionen enthält die offizielle Funktionsliste.
| Funktion | Ergebnis | Typischer Einsatz |
|---|---|---|
COUNT(*) |
Anzahl aller Zeilen | Datensätze zählen |
COUNT(Spalte) |
Anzahl der Nicht-NULL-Werte |
Ausgefüllte Angaben zählen |
SUM(Spalte) |
Summe numerischer Werte | Umsatz addieren |
AVG(Spalte) |
Arithmetischer Mittelwert | Durchschnittspreis berechnen |
MIN(Spalte) / MAX(Spalte) |
Kleinster / größter Wert | Wertebereich bestimmen |
COUNT_BIG(*) |
Zeilenanzahl als bigint |
Sehr große Ergebnismengen zählen |
Beispieldaten für alle SQL-Abfragen
Die folgenden Beispiele verwenden eine temporäre Tabelle. Führe den Block einmal in SQL Server Management Studio oder einem anderen T-SQL-Editor aus und lasse die weiteren Abfragen im selben Verbindungsfenster laufen. Das Skript benötigt keine vorhandenen Geschäftstabellen. DROP TABLE IF EXISTS setzt beim erneuten Ausführen nur diese temporäre Tabelle der Sitzung zurück.
DROP TABLE IF EXISTS #UmsatzDemo;
CREATE TABLE #UmsatzDemo (
UmsatzID int NOT NULL PRIMARY KEY,
Region nvarchar(20) NOT NULL,
Kunde nvarchar(40) NULL,
Umsatzdatum date NOT NULL,
Betrag decimal(10, 2) NULL
);
INSERT INTO #UmsatzDemo
(UmsatzID, Region, Kunde, Umsatzdatum, Betrag)
VALUES
(1, N'Nord', N'Anna', '20250110', 100.00),
(2, N'Nord', N'Ben', '20250111', 50.00),
(3, N'Nord', N'Anna', '20250202', NULL),
(4, N'Süd', N'Cara', '20250110', 80.00),
(5, N'Süd', N'Dan', '20250211', 120.00),
(6, N'Süd', NULL, '20250212', 70.00);
Die sechs Zeilen enthalten bewusst einen fehlenden Betrag, einen fehlenden Kundennamen und einen doppelt vorkommenden Namen. Dadurch werden die Unterschiede zwischen den Funktionen sichtbar.
COUNT, COUNT(DISTINCT) und COUNT_BIG richtig verwenden
COUNT(*) zählt alle sechs Zeilen. COUNT(Betrag) zählt nur die fünf Zeilen mit einem Betrag. COUNT(DISTINCT Kunde) zählt die vier verschiedenen, nicht leeren Kundennamen; der doppelte Name „Anna“ zählt einmal. COUNT_BIG(*) liefert wie COUNT(*) die Zeilenzahl, aber als bigint statt int.
SELECT COUNT(*) AS AlleZeilen,
COUNT(Betrag) AS ZeilenMitBetrag,
COUNT(DISTINCT Kunde) AS VerschiedeneKunden,
COUNT_BIG(*) AS AlleZeilenBigint
FROM #UmsatzDemo;
Erwartetes Ergebnis: 6, 5, 4 und 6. Auf sehr großen Datenmengen ist COUNT_BIG sinnvoll, weil COUNT nur einen int-Wert zurückgibt und bei Überschreitung seines Wertebereichs überlaufen kann. Es ist jedoch nicht automatisch schneller. Details stehen in der COUNT-Referenz und der COUNT_BIG-Referenz.
SUM und AVG: Summen und Durchschnitt berechnen
SUM addiert numerische Werte; AVG teilt die Summe durch die Zahl der nicht leeren Werte. In den Beispieldaten beträgt die Summe 420,00. Für den Durchschnitt zählt der fehlende Betrag nicht mit: 420 / 5 = 84,00.
SELECT SUM(Betrag) AS Gesamtbetrag,
AVG(Betrag) AS Durchschnittsbetrag
FROM #UmsatzDemo;
Achte auf den Datentyp. AVG über einer int-Spalte liefert in SQL Server wieder int; Nachkommastellen gehen dabei verloren. Wenn du einen Dezimalwert brauchst, konvertiere vor der Berechnung:
SELECT AVG(CONVERT(decimal(10, 2), Wert)) AS Durchschnitt
FROM (VALUES (1), (2)) AS Beispiel(Wert);
Das Ergebnis ist 1,500000. Auch bei großen Summen solltest du den Datentyp und mögliche Überläufe berücksichtigen. Bei decimal hängt die Ergebnisgenauigkeit von den T-SQL-Typregeln ab; bei float und real sind Ergebnisse näherungsweise. Die Microsoft-Dokumentation zu AVG beschreibt die Rückgabetypen.
MIN und MAX: kleinsten und größten Wert finden
MIN und MAX funktionieren unter anderem für Zahlen und Datumswerte. Auch sie lassen NULL aus:
SELECT MIN(Betrag) AS KleinsterBetrag,
MAX(Betrag) AS GroessterBetrag,
MIN(Umsatzdatum) AS ErstesDatum,
MAX(Umsatzdatum) AS LetztesDatum
FROM #UmsatzDemo;
Hier sind 50,00 und 120,00 die Betragsgrenzen. Bei MIN(Kunde) oder MAX(Kunde) hängt die Sortierung von der Kollation ab; das ist keine verlässliche Methode, um „den ersten“ oder „den letzten“ Kunden nach Datum zu finden.
GROUP BY: Ergebnisse pro Region ausgeben
Mit GROUP BY berechnest du Aggregate für jede Region. Nicht aggregierte Ausdrücke in der SELECT-Liste müssen zur Gruppierung passen. GROUP BY sortiert das Ergebnis nicht; für eine feste Reihenfolge brauchst du ORDER BY.
SELECT Region,
COUNT(*) AS AnzahlZeilen,
COUNT(Betrag) AS AnzahlMitBetrag,
SUM(Betrag) AS Gesamtbetrag,
AVG(Betrag) AS Durchschnittsbetrag
FROM #UmsatzDemo
GROUP BY Region
ORDER BY Region;
Für „Nord“ erhältst du drei Zeilen, zwei Beträge, 150,00 Summe und 75,00 Durchschnitt. Für „Süd“ sind es drei Zeilen, drei Beträge, 270,00 Summe und 90,00 Durchschnitt. Würde Region selbst NULL enthalten, bildeten diese Zeilen gemeinsam eine eigene Gruppe.
WHERE oder HAVING: Zeilen und Gruppen unterscheiden
WHERE filtert Eingabezeilen vor der Gruppierung. HAVING filtert die gebildeten Gruppen anhand einer Bedingung, häufig mit einem Aggregat. Das folgende Beispiel berücksichtigt nur Umsätze ab 2025-02-01 und zeigt davon nur Regionen mit mindestens 100,00 Summe:
SELECT Region, SUM(Betrag) AS Gesamtbetrag
FROM #UmsatzDemo
WHERE Umsatzdatum >= '20250201'
GROUP BY Region
HAVING SUM(Betrag) >= 100
ORDER BY Region;
„Nord“ hat im Februar nur einen NULL-Betrag und erfüllt die Bedingung nicht. „Süd“ erscheint mit 190,00. Eine Aggregatfunktion wie SUM(Betrag) gehört nicht direkt in die WHERE-Bedingung derselben Gruppierungsabfrage. Mehr zu den Klauseln findest du in der GROUP-BY- und HAVING-Dokumentation.
Nach Jahr und Monat gruppieren
Für Monatsberichte solltest du Jahr und Monat gemeinsam gruppieren. Nur nach der Monatsnummer zu gruppieren würde beispielsweise Januar 2025 und Januar 2026 vermischen:
SELECT YEAR(Umsatzdatum) AS Jahr,
MONTH(Umsatzdatum) AS Monat,
COUNT(*) AS AnzahlZeilen,
SUM(Betrag) AS Gesamtbetrag
FROM #UmsatzDemo