![]() |
Datenbanken:
|
|
| |
| E-Mail: |
|---|
Bei Fragen zu diesem Beitrag bitte die Autorin des Beitrags kontaktieren!
Viele Anwendungen brauchen nur einen Datensatz zu einem bestimmten Kriterium, beispielsweise die letzte Bestellung pro Benutzer. SELECT DISTINCT hilft bei dieser Aufgabenstellung nicht, da es nur Datensätze unterdrückt, die in allen auszugebenden Feldern identische Werte haben. Es muss also ein anderes Mittel geben dieses Problem zu lösen.
GROUP BY fasst Zeilen mit identischen Werten in den aufgelisteten Feldern zusammen. Aber was passiert mit den nicht nach GROUP BY aufgelisteten Feldern, die zurückgegeben werden? Das Datenbanksystem weiß nicht, welchen der gefundenen Werte es zurückgeben soll. Die meisten Datenbanksysteme geben deswegen einen Syntaxfehler aus, MySQL akzeptiert eine solche Abfrage normalerweise (Ausnahme: im ANSI-Modus), gibt jedoch zufällig einen der möglichen Werte zurück. Da jedoch zufällige Werte genauso nützlich sind wie gar keine Werte, gibt es die sogenannten Aggregatsfunktionen wie min(), max(), avg(), count() und je nach nach Datenbanksystem weitere. Diese Funktionen erlauben es, die Werte nach denen nicht gruppiert wird, auf genau einen pro Spalte und Gruppe zu reduzieren.
Das Ergebnis einer Aggregatsfunktion ist zum Ausführungszeitpunkt von WHERE noch nicht bekannt. Deswegen kann das Ergebnis nicht auf diese Art eingeschränkt werden. Um dieses Problem zu lösen gibt es HAVING. Die Anwendung entspricht exakt der von WHERE, jedoch sind zum Zeitpunkt der Ausführung von HAVING bereits die Inhalte der Aggregatsfunktionsspalten bekannt. HAVING sollte jedoch nur für diesen Zweck verwendet werden, da keine Indizes mehr benutzt werden können und es somit langsamer als WHERE ist.
<!--%% Query1 %%-->
SELECT benutzerid,
max(datum) as dat,
count(datum) as anzBestellungen
FROM bestellungen
GROUP BY benutzerid
ORDER BY anzBestellungen
<!--%% Query2 %%-->
SELECT benutzerid,
max(datum) as dat,
count(distinct datum) as anzBestellungen
FROM bestellungen
GROUP BY benutzerid
ORDER BY anzBestellungen
<!--%% Query3 %%-->
SELECT benutzerid,
max(datum) as dat,
count(distinct datum) as anzBestellungen
FROM bestellungen
GROUP BY benutzerid
HAVING dat > '31.12.2001'
ORDER BY anzBestellungen
<!--%% Datenbestand %%-->
benutzerid bestellid datum
1 1 11.09.2001
1 2 11.09.2001
1 7 10.11.2002
2 3 01.10.2001
2 5 05.11.2001
2 6 03.12.2001
3 4 11.10.2001
3 8 15.11.2002
3 9 17.11.2002
<!--%% Ausgabe1 %%-->
benutzerid dat anzBestellungen
1 10.11.2002 3
2 03.12.2001 3
3 17.11.2002 3
<!--%% Ausgabe2 %%-->
benutzerid dat anzBestellungen
1 10.11.2002 2
2 03.12.2001 3
3 17.11.2002 3
<!--%% Ausgabe3 %%-->
benutzerid dat anzBestellungen
1 10.11.2002 2
3 17.11.2002 3
Query 1 listet zu jedem Benutzer das Datum seiner letzten Bestellung
(max(datum)) sowie die Anzahl seiner bisherigen Bestellungen.
(count(datum)) auf.
Bei Query 2 unterdrückt distinct
dabei identische Werte im Datum und zählt nur Bestellungen von verschiedenen Tagen.
distinct ist in den meisten Aggregatsfunktion nicht
sinnvoll, da es, wie zb bei max(), keine Rolle spielt, welcher von 2 identischen Werten ausgegeben
wird.
Query 3 schließt zusätzlich alle Benutzer aus, deren letzte Bestellung vor
dem 31.12.2001 war. Dies geschieht mit HAVING dat > '31.12.2001'.
anzBestellungen umfasst jedoch alle Bestellungen, auch die
vor dem 31.12.2001, da HAVING entweder die ganze Resultatszeile
behält oder wegwirft. Bei diesem Beispiel gibt es gleich zwei datenbanksystemspezifische
Fallstricke. Entweder muss wie beim Beispiel im HAVING das Alias verwendet werden oder aber
es muss direkt die Funktion (max(datum)), wie sie im SELECT erscheint, benutzt werden. Beide Versionen
sind äquivalent vom Ergebnis her, von den Datenbanksystemen wird jedoch nur eine Variante unterstützt. Dazu kommt, dass Datumsformate je nach Datenbanksystem und Umgebungseinstellung variieren und möglicherweise nicht so funktionieren wie im Beispiel. Z.B. ist bei einer Standardinstallation von PostgreSQL das Datumsformat 2002-12-31.
Es ist auf diesem Weg nicht möglich, weitere Spalten wie den Rechnungsbetrag zum mit max(datum) selektierten Datensatz anzuzeigen. Die einzelnen Spalten einer Zeile stehen in keinerlei Beziehung zueinander außer durch die Gruppierung. Wenn sowas gewünscht ist, muss es über Subselects oder über Join-Konstrukte gelöst werden.
Die folgenden Stellen werden empfohlen, um das obige Beispiel besser zu verstehen, oder um weitere Möglichkeiten und Details zu erfahren.
Mögliche Aggregatsfunktionen in MySQL
GROUP BY in PostgreSQL
Aggregatsfunktionen in PostgreSQL
Probekapitel von SQL in a Nutshell, ua zu Aggregatsfunktionen