CodeGym /Kurse /SQL SELF /Verwendung von Subqueries in HAVING zur Filterung aggregi...

Verwendung von Subqueries in HAVING zur Filterung aggregierter Daten

SQL SELF
Level 14 , Lektion 2
Verfügbar

Manchmal reicht es nicht, Daten einfach nur zu gruppieren und das Ergebnis zu filtern – manchmal braucht man noch zusätzliche Logik. Zum Beispiel willst du den Durchschnitt der Noten von Studierenden in einer Gruppe mit irgendeinem externen Kriterium vergleichen. Hier kommt HAVING mit Subqueries ins Spiel – ein mächtiges Tool, das dir erlaubt, smartere Entscheidungen direkt im SQL-Query zu treffen.

Erinnern wir uns an HAVING

Lass uns auf Subqueries schauen, die zusammen mit HAVING verwendet werden, um Daten auf aggregierter Ebene zu filtern. Warum? WHERE filtert einzelne Zeilen, aber HAVING kommt erst nach der Gruppierung ins Spiel – das ist eine andere Analyse-Ebene, die dir mehr Möglichkeiten gibt.

Bevor wir tiefer in die Kombination von Subqueries und HAVING eintauchen, lass uns kurz auffrischen, was HAVING ist und wie es sich von WHERE unterscheidet.

  • WHERE filtert Zeilen vor der Gruppierung (GROUP BY).
  • HAVING filtert Daten nach der Aggregation, wenn die Daten schon gruppiert sind.

Stell dir vor, du analysierst Studierende und deren Noten. Mit WHERE kannst du Studierende mit bestimmten Mindestnoten ausschließen, aber mit HAVING kannst du ganze Gruppen von Studierenden basierend auf deren Durchschnitts- oder Maximalnote ausschließen.

Beispieldaten

Hier ist eine Tabelle mit Beispiel-Studierenden:

Tabelle students:

student_id student_name department grade
1 Alex Physics 80
2 Maria Physics 85
3 Dan Math 90
4 Lisa Math 60
5 John History 70

Beispiel für HAVING (ohne Subqueries)

SELECT department, AVG(grade) AS avg_grade
FROM students
GROUP BY department
HAVING AVG(grade) > 75;

Ergebnis:

department avg_grade
Physics 82.5
Math 75.0

Der Fachbereich "History" ist nicht dabei, weil der Durchschnitt dort unter 75 liegt. Easy, oder? Jetzt bringen wir ein bisschen Magie mit Subqueries rein. Im nächsten Beispiel filtern wir zum Beispiel im Vergleich zum Gesamtdurchschnitt aller Fachbereiche.

Subqueries in HAVING

Subqueries in HAVING sind eine super Möglichkeit, mehr Flexibilität beim Filtern von aggregierten Daten zu bekommen. Damit kannst du Aggregatwerte wie Durchschnitt oder Maximum mit berechneten Werten aus anderen Teilen der Datenbank vergleichen. Kurz gesagt: Du kannst checken, ob dein Ergebnis "besser als der Durchschnitt" ist.

Beispiel: Fachbereiche nach Durchschnittsnote filtern

Angenommen, wir wollen die Fachbereiche finden, in denen die Studierenden besser als der Rest sind – also wo der Durchschnitt höher als der Uni-Durchschnitt ist.

Hier sind unsere Daten:

Tabelle students:

student_id student_name department grade
1 Alex Physics 80
2 Maria Physics 85
3 Dan Math 90
4 Lisa Math 60
5 John History 70

Erstmal holen wir uns den Durchschnitt aller Studierenden:

SELECT AVG(grade) AS university_avg
FROM students;

Jetzt nutzen wir eine Subquery in HAVING:

SELECT department, AVG(grade) AS avg_grade
FROM students
GROUP BY department
HAVING AVG(grade) > (SELECT AVG(grade) FROM students);

Ergebnis:

department avg_grade
Physics 82.5

Was passiert hier?

  1. Die Subquery (SELECT AVG(grade) FROM students) berechnet den Uni-Durchschnitt – hier ist das 77.
  2. Der Hauptquery gruppiert die Studierenden nach Fachbereich und berechnet für jeden den Durchschnitt.
  3. HAVING vergleicht den Durchschnitt des Fachbereichs mit dem Uni-Durchschnitt und gibt nur die Fachbereiche aus, die drüber liegen.

Vergleich: WHERE vs. HAVING

Um den Unterschied zu verstehen, stell dir vor, du willst nur die Studierenden auswählen, deren Noten über dem Durchschnitt liegen. Das geht nur mit WHERE:

SELECT name, grade
FROM students
WHERE grade > (SELECT AVG(grade) FROM students);

Ergebnis (wenn wir die Tabelle von oben nehmen):

name grade
Alex 80
Maria 85
Dan 90

Wenn du aber wissen willst, in welchen Fachbereichen der Durchschnitt der Studierenden über dem Uni-Durchschnitt liegt, kommst du ohne HAVING nicht aus – weil du nicht Zeilen, sondern Gruppen filterst:

SELECT department, AVG(grade) AS avg_grade
FROM students
GROUP BY department
HAVING AVG(grade) > (SELECT AVG(grade) FROM students);

Ergebnis:

department avg_grade
Physics 82.5

Kurz gesagt:

  • WHERE arbeitet mit einzelnen Zeilen vor der Gruppierung.
  • HAVING filtert Gruppen, nachdem sie aggregiert wurden.

Beispiel: Arbeiten mit mehreren Aggregaten

Schauen wir uns noch einen Fall an. Angenommen, wir haben die Tabelle students, in der Noten und Fachbereiche der Studierenden gespeichert sind:

Tabelle students:

name grade department
Alex 80 Physics
Maria 85 Physics
Dan 90 Math
Olga 95 Math
Ivan 70 History
Nina 75 History

Jetzt wollen wir die Fachbereiche finden, wo:

  1. Der Durchschnitt der Noten höher als der Uni-Durchschnitt ist.
  2. Die Maximalnote im Fachbereich über 90 liegt.

Dafür schreiben wir diesen Query:

SELECT department, AVG(grade) AS avg_grade, MAX(grade) AS max_grade
FROM students
GROUP BY department
HAVING AVG(grade) > ( SELECT AVG(grade) FROM students )
   AND MAX(grade) > 90;

Was passiert in diesem Query:

  • AVG(grade) > (SELECT AVG(grade) FROM students) – wir checken, ob der Fachbereich im Schnitt besser ist als der Rest.
  • MAX(grade) > 90 – es gibt jemanden, der richtig abgeräumt hat.

Ergebnis:

department avg_grade max_grade
Math 92.5 95

Der Fachbereich "Math" ist der einzige, der sowohl einen höheren Durchschnitt als auch eine Top-Note über 90 hat.

Beispiel: Gruppen mit minimaler Abweichung auswählen

Angenommen, du willst Gruppen finden, bei denen die Differenz zwischen der höchsten und niedrigsten Note kleiner ist als die Differenz in der Uni insgesamt.

Hier ist die Tabelle students, mit der wir arbeiten:

name grade department
Alex 80 Physics
Maria 85 Physics
Dan 90 Math
Olga 95 Math
Ivan 70 History
Nina 75 History

Wir teilen die Aufgabe in Schritte:

  1. Erst berechnen wir die Differenz Max-Min für die ganze Uni:
    SELECT MAX(grade) - MIN(grade) AS range_university
    FROM students;
    
  2. Jetzt bauen wir den Hauptquery und verbinden ihn mit dieser Subquery:
SELECT department, MAX(grade) - MIN(grade) AS range_department
FROM students
GROUP BY department
HAVING (MAX(grade) - MIN(grade)) < ( SELECT MAX(grade) - MIN(grade) FROM students );

Ergebnis des Queries:

department range_department
Physics 5
Math 5

Die Gruppen "Physics" und "Math" haben stabilere Noten – deren Streuung ist kleiner als im Uni-Schnitt.

Optimierung von Queries mit HAVING und Subqueries

Denk dran: Verschachtelte Subqueries können die Performance stark beeinflussen, besonders bei großen Datenbanken. Hier ein paar Tipps:

Nutze Indizes. Wenn die Subquery auf einer Spalte läuft, die in WHERE oder JOIN verwendet wird, stell sicher, dass es dort einen Index gibt.

Vermeide Datenüberflutung. Wenn die Subquery zu viele Zwischenergebnisse liefert, teile sie in Schritte auf oder nutze temporäre Tabellen.

Profilier deine Queries mit EXPLAIN. Schau immer nach, wie PostgreSQL deinen Query ausführt. Wenn du siehst, dass die Subquery mehrfach ausgeführt wird, denk über Optimierung nach.

Vergleiche mit CTE. In manchen Fällen ist WITH (Common Table Expressions) schneller und lesbarer. Aber dazu mehr in den nächsten Vorlesungen :P

Kombinieren von Subqueries, HAVING und GROUP BY

Mit Subqueries in HAVING kannst du noch komplexere Filter bauen, besonders wenn du gleichzeitig Aggregatwerte, Durchschnitte und andere Metriken berücksichtigen willst. Das hilft dir, spannende Insights in echten Daten zu finden.

Beispiel: Vergleich von Fachbereichen nach Durchschnittsnote und Studierendenzahl

Angenommen, du willst Fachbereiche auswählen, wo:

  1. Der Durchschnitt über dem Uni-Durchschnitt liegt.
  2. Die Anzahl der Studierenden größer ist als im Fachbereich mit dem niedrigsten Durchschnitt.

Hier die Ausgangstabelle students:

name grade department
Alex 80 Physics
Maria 85 Physics
Dan 90 Math
Olga 95 Math
Ivan 70 History
Nina 75 History
Oleg 60 History

Query:

SELECT department, AVG(grade) AS avg_grade, COUNT(*) AS student_count
FROM students
GROUP BY department
HAVING AVG(grade) > ( SELECT AVG(grade) FROM students )
   AND COUNT(*) > (
       SELECT COUNT(*)
       FROM students
       GROUP BY department
       ORDER BY AVG(grade)
       LIMIT 1
   );

Dieser Query zeigt, wie man Subqueries in HAVING und GROUP BY kombiniert, um nach mehreren Kriterien zu analysieren. Ergebnis:

department avg_grade student_count
Physics 82.5 2
Math 92.5 2

Der Fachbereich History ist nicht dabei, weil er den niedrigsten Durchschnitt und die wenigsten Studierenden hat. Physics und Math – beide sind über dem Durchschnitt, sowohl bei den Noten als auch bei der Anzahl.

Typische Fehler und wie man sie vermeidet

Fehler mit NULL. Wenn deine Daten NULL enthalten, können Subqueries mit HAVING unerwartete Ergebnisse liefern. Nutze COALESCE, um solche Fälle zu behandeln:

SELECT AVG(grade)
FROM students 
WHERE grade IS NOT NULL;

Zu viele Daten in der Subquery. Wenn die Subquery zu viele Ergebnisse liefert, leidet die Performance. Präzisiere immer die Bedingungen in der Subquery.

Falsches Verständnis der Ausführungsreihenfolge. Denk dran: HAVING läuft nach der Gruppierung, aber Subqueries können schon vor dem Hauptquery laufen.

Fehlende Indizes. Wenn Spalten, die in der Subquery genutzt werden, nicht indiziert sind, wird der Query deutlich langsamer.

Subqueries in HAVING eröffnen dir viele Möglichkeiten, Daten auf Aggregat-Ebene zu analysieren. Du kannst Gruppen nach komplexen Bedingungen filtern, Ergebnisse zwischen Gruppen vergleichen und richtig coole analytische Queries bauen. Glückwunsch, jetzt bist du bereit, das in echten Projekten einzusetzen!

Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION