Aggregierungsfunktionen bilden einen essentiellen Bestandteil von SQL und ermögliche eine Vielzahl von komplexen Operationen. Im Gegensatz zu 'normalen' Funktionen arbeiten Aggregierungsfunktionen immer auf einem Set von Datensätzen - aus vielen Eingabedatensätzen wird somit ein Wert erzeugt.
COUNT() ist eine der einfachsten Aggregierungsfunktionen und kann im einfachsten Fall wie folgt verwendet werden:
test=# SELECT COUNT(*) FROM t_person;
count
-------
7
(1 row)
PostgreSQL meldet, dass sich sieben Datensätze in der Datenbank befinden doch stimmt das wirklich?
test=# SELECT COUNT(name) FROM t_person;
count
-------
6
(1 row)
Diesmal meldet das System nur sechs Datensätze. Der Grund für diesen kleinen aber überaus wesentlichen Unterschied liegt auf der Hand: Im ersten Fall zählen wir die Anzahl der Datensätze in der Tabelle. Im zweiten Fall zählen wir die Anzahl der gültigen Datensätze in der Spalte 'name'. Da die Spalte einen NULL-Wert enthält, wird ein Datensatz weniger gefunden. Da NULL-Werte als undefiniert gelten, werden Sie von COUNT ganz einfach ignoriert.
In vielen Fällen werden Aggregierungsfunktionen in Kombination mit einer GROUP BY Clause verwendet:
test=# SELECT name, COUNT(*)
FROM t_person
WHERE name IS NOT NULL
GROUP BY name;
name | count
---------------+-------
Josef Fischer | 1
Anton Jelinek | 1
Markus Eisner | 1
Leo Lechner | 1
Gaika Huber | 2
(5 rows)
Wenn wir neben dem Output der Aggregierungsfunktion noch andere Felder benötigen, müssen wir all diese Felder in die GROUP BY Clause aufnehmen. Das ist von entscheidender Bedeutung, da das SQL Statement sonst formal nicht korrekt ist:
test=# SELECT name, COUNT(*)
FROM t_person
WHERE name IS NOT NULL
GROUP BY name
ORDER BY count DESC;
name | count
---------------+-------
Gaika Huber | 2
Josef Fischer | 1
Anton Jelinek | 1
Markus Eisner | 1
Leo Lechner | 1
(5 rows)
Unser kleines Beispiel zeigt, wie oft ein Name in der Tabelle vorkommt. Dabei wollen wir, dass die Anzahl absteigend sortiert wird.
PostgreSQL ermöglicht es auch, mehrere Aggregierungsfunktionen in einer SELECT-Clause zu verwenden. Wollen wir beispielsweise die größte Sozialversicherungsnummer und die Anzahl der Datensätze auf einmal errechnen, sieht das dann so aus:
test=# SELECT MAX(svnr), COUNT(name) FROM t_person; max | count ------+------- 9876 | 6 (1 row)
Wenn wir die größte Sozialversicherungsnummer und die Anzahl der Werte pro Namen auswerten wollen, sieht das dann so aus:
test=# SELECT name, MAX(svnr), COUNT(name)
FROM t_person
GROUP BY name;
name | max | count
---------------+----------+-------
| 34567 | 0
Josef Fischer | 2456 | 1
Anton Jelinek | 24567 | 1
Markus Eisner | 123456 | 1
Leo Lechner | 12345678 | 1
Gaika Huber | 9876 | 2
(6 rows)
test=# SELECT * FROM t_person WHERE name = 'Gaika Huber';
svnr | name
------+-------------
2345 | Gaika Huber
9876 | Gaika Huber
(2 rows)
Aus diesem Ergebnis ist ersichtlich, dass der Name 'Gaika Huber' zweimal vorkommt. Zu diesem Namen gibt es zwei Sozialversicherungsnummern - wie erwartet wird die größere Nummer in der max-Spalte gelistet.
Im nächsten Schritt wollen wir versuchen, nur Datensätze auszugeben, die öfter als einmal vorkommen. Viele Benutzer versuchen es im ersten Schritt so:
test=# SELECT name, COUNT(*) FROM t_person WHERE name IS NOT NULL AND count > 1 GROUP BY name ORDER BY count DESC; ERROR: column "count" does not exist
Das funktioniert nicht, da es nicht möglich ist, das Ergebnis einer Aggregierungsfunktion in einer WHERE Clause zu verwenden. WHERE-Clauses operieren immer auf einzelnen Datensätzen - da Aggregierungsfunktionen mehr als einen Datensatz bearbeiten, können Restriktionen daher nicht in die WHERE Clause gepackt werden.
Um diese Operation dennoch zu ermöglichen, schreibt SQL sogenannte HAVING-Clauses vor:
test=# SELECT name, COUNT(*)
FROM t_person
WHERE name IS NOT NULL
GROUP BY name HAVING count(*) > 1;
name | count
-------------+-------
Gaika Huber | 2
(1 row)
Im Gegensatz zu einer WHERE-Clause operiert eine HAVING-Clause immer auf dem Ergebnis einer Aggregierungsfunktion. In unserem Fall kommt nur eine Zeile zurück.
Oft ertappt man sich dabei, in einer HAVING Clause einen Spaltennamen direkt zu verwenden. Der ANSI SQL Standard verbietet das allerdings:
test=# SELECT name, COUNT(*) FROM t_person WHERE name IS NOT NULL GROUP BY name HAVING count > 1; ERROR: column "count" does not exist
'count > 1' ist nicht gestattet - es ist unbedingt notwendig, die Aggregierungsfunktion noch einmal komplett anzuschreiben. Die Implementierung dieses Features würde kein großes Problem darstellen - das Entwickler Team versucht sich aber so weit wie möglich an den Standard zu halten.