Premium

Der HAVING-Operator

Wir haben uns bereits die Query angeschaut, mit der wir den durchschnittlichen Mietpreis pro Unterkunftstyp ermitteln:

MySQL 8.1
SELECT home_type, AVG(price) as avg_price FROM Rooms
GROUP BY home_type
home_typeavg_price
Private room89.4286
Entire home/apt148.6667
Shared room40

Bauen wir die Query so um, dass im Ergebnis nur die Gruppen erscheinen, deren Durchschnittspreis größer als 50 ist.

Aus dem bisherigen Wissen entsteht schnell die Versuchung, dafür WHERE zu verwenden. Beim Versuch, eine solche Query auszuführen, liefert das DBMS aber zwangsläufig einen Fehler und weist darauf hin, dass die Syntax WHERE avg_price > 50 so nicht korrekt ist.

MySQL 8.1
SELECT home_type, AVG(price) as avg_price FROM Rooms
GROUP BY home_type
WHERE avg_price > 50

Vorab so viel: Zum Filtern von Gruppen brauchen wir den Operator HAVING:

MySQL 8.1
SELECT home_type, AVG(price) as avg_price FROM Rooms
GROUP BY home_type
HAVING avg_price > 50
MySQL 8.1
SELECT home_type, AVG(price) as avg_price FROM Rooms
GROUP BY home_type
HAVING AVG(price) > 50
home_typeavg_price
Private room89.4286
Entire home/apt148.6667

In PostgreSQL stehen in SELECT deklarierte Aliase in HAVING nicht zur Verfügung.

Ausführungsreihenfolge einer SQL-Query

Aber warum konnten wir WHERE nicht verwenden und wozu braucht es überhaupt einen eigenen Operator dafür? Die Antwort liegt in der Reihenfolge, in der eine SQL-Query ausgeführt wird.

Schema der Ausführungsreihenfolge einer SQL-Query

Unsere erste Query war falsch, weil wir das Feld avg_price der entstehenden Gruppen schon vor deren Bildung benutzen wollten – WHERE wird nämlich vor der Gruppierung ausgeführt.

WHERE weiss zum Zeitpunkt seiner Ausführung also nichts von der späteren Gruppierung; es arbeitet ausschließlich mit den Datensätzen aus der Tabelle. So können wir damit zum Beispiel die Datensätze der Tabelle Rooms nach Preis filtern, bevor die Gruppierung greift, und erst danach den Durchschnittspreis der Gruppen der übrigen Unterkünfte berechnen:

MySQL 8.1
SELECT home_type, AVG(price) as avg_price FROM Rooms
WHERE price > 50
GROUP BY home_type
home_typeavg_price
Private room96.875
Entire home/apt148.6667

Allgemeine Struktur einer Query mit HAVING

MySQL 8.1
SELECT [konstanten, aggregat_funktionen, gruppierungs_felder]
FROM tabellen_name
WHERE zeilen_einschraenkungs_bedingungen
GROUP BY gruppierungs_felder
HAVING bedingung_fuer_zeilen_nach_gruppierung
ORDER BY sortier_bedingung

Anwendungsbeispiel für HAVING

Ermitteln wir als Beispiel den Mindestpreis pro Unterkunftstyp mit Fernseher. Uns interessieren dabei nur Unterkunftstypen, zu denen mindestens 5 Wohnungen gehören.

Um zu diesem Ergebnis zu kommen, müssen wir:

  • zuerst alle Daten aus der Tabelle holen

    MySQL 8.1
    SELECT ... FROM Rooms;
    
  • dann aus allen Datensätzen der Tabelle Rooms nur die uns interessierenden auswählen, also nur Unterkünfte mit Fernseher

    MySQL 8.1
    SELECT ... FROM Rooms
    WHERE has_tv = True
    
  • anschließend die Datensätze der Unterkünfte nach Typ gruppieren

    MySQL 8.1
    SELECT ... FROM Rooms
    WHERE has_tv = True
    GROUP BY home_type
    
  • danach die entstandenen Gruppen nach unserer Bedingung filtern. Uns interessieren Gruppen mit mindestens 5 Vertretern

    MySQL 8.1
    SELECT ... FROM Rooms
    WHERE has_tv = True
    GROUP BY home_type
    HAVING COUNT(*) >= 5
    
  • und zum Schluss schauen, was in der Aufgabe verlangt ist, und die nötigen Felder ausgeben. In unserem Fall brauchen wir den Namen des Unterkunftstyps und seinen Mindestpreis.

    MySQL 8.1
    SELECT home_type, MIN(price) as min_price FROM Rooms
    WHERE has_tv = True
    GROUP BY home_type
    HAVING COUNT(*) >= 5;