23rd Jun 2022 11 Leseminuten Eine Einführung in die Verwendung von SQL-Aggregatfunktionen mit JOINs Francisco Claria Aggregatfunktionen JOIN Inhaltsverzeichnis Was bewirken SQL-Aggregatfunktionen? Parent-Child JOINs Aggregat + GROUP BY + JOIN MIN + GRUPPE NACH + JOIN SUMME + GRUPPIERUNG NACH + VERKNÜPFUNG COUNT + GROUP BY + JOIN DURCHSCHNITT + GRUPPIERUNG NACH + VERKNÜPFUNG AVG + GRUPPE NACH + JOINS Ergebnisse filtern Verwendung des JOIN-Prädikats Verwendung von WHERE-Bedingungen HAVING-Bedingungen verwenden Der Umgang mit NULLs Ein letzter Tipp zur Arbeit mit SQL-Aggregatfunktionen Zuvor haben wir über die Verwendung von SQL-Aggregatfunktionen mit der GROUP BY-Anweisung. Regelmäßige Leser unseres Blogs werden sich auch an unseren letzten Tutorium über JOINs. Wenn Sie bei beiden Themen etwas eingerostet sind, sollten Sie diese nachlesen, bevor Sie mit diesem Artikel fortfahren. Wir werden nämlich weiter in das Thema eintauchen. Aggregat-Funktionen indem sie mit JOINs kombiniert werden. Durch dieses Duo werden die Möglichkeiten der SQL-Aggregatfunktionen voll ausgeschöpft und wir können Berechnungen für mehrere Tabellen in einer einzigen Abfrage durchführen. Was bewirken SQL-Aggregatfunktionen? Hier finden Sie einen kurzen Überblick über die gängigsten SQL-Aggregatfunktionen: FUNCTIONPURPOSEEXAMPLE MIN Returns the smallest value in a column. SELECT MIN(column) FROM table_name MAX Returns the largest value in a column SELECT MAX(column) FROM table_name SUM Calculates the sum of all numeric values in a column SELECT SUM(column) FROM table_name AVG Returns the average value for a column SELECT AVG(column) FROM table_name COUNT(column) Counts the number of non-null values in a column SELECT COUNT(column) FROM table_name COUNT(*) Counts the total number of rows (including NULLs) in a column SELECT COUNT(*) FROM table_name Es ist auch wichtig, sich daran zu erinnern, dass die Anweisung GROUP BY, wenn sie mit Aggregaten verwendet wird, Werte berechnet, die nach Spalten gruppiert wurden. (Für weitere Informationen siehe Einsteigerhandbuch für SQL-Aggregatfunktionen.) Wir können GROUP BY mit jeder der oben genannten Funktionen verwenden. Im folgenden Beispiel verwenden wir beispielsweise die Funktion MIN(): SELECT MIN(column_name) FROM table_name GROUP BY group_column Dies würde den Mindestwert abrufen, der in column_name für jeden Satz von Werten in einer Gruppe auf der Grundlage der Spalte group_column gefunden wurde. Die gleiche Idee gilt für die Funktionen MAX, SUM, AVG und COUNT. Parent-Child JOINs Kommen wir nun zu einigen häufigen Situationen, in denen Sie JOINs nach Gruppen mit Aggregatfunktionen verwenden. Wenn Sie A Beginner's Guide to SQL Aggregate Functions gelesen haben, wird Ihnen das folgende Diagramm bereits bekannt sein: Wenn Sie dieses Modell schon einmal verwendet haben (z. B. bei den Beispielen aus dem vorigen Artikel), stellen Sie bitte sicher, dass Sie alle vorhandenen Datensätze aus Ihrer Tabelle löschen. Dies können Sie tun, indem Sie die folgenden Befehle ausführen: TRUNCATE cities; TRUNCATE users; Geben wir nun einige neue Daten in die Tabellen ein: INSERT INTO `cities` VALUES (1,'Miami'), (2,'Orlando'), (3,'Las Vegas'), (4,'Coyote Springs'); INSERT INTO `users` VALUES (1,1,'John','Doe',22), (2,1,'Albert','Thomson',15), (3,2,'Robert','Ford',65), (4,3,'Samantha','Simpson',9), (5,2,'Carlos','Bennet',42), (6,2,'Mirtha','Lebrand',81), (7,3,'Alex','Gomez',31); Wir haben also eine Tabelle namens users und eine weitere Tabelle namens cities. Diese beiden Tabellen haben etwas gemeinsam: einen numerischen Wert für die Stadtkennung. Dieser Wert ist in der Spalte id in der Tabelle cities und in der Spalte city_id in der Tabelle users Tabelle gespeichert. Die Spalte city_id enthält einen Verweis (auch bekannt als Fremdschlüssel), der einen Benutzerdatensatz mit einer Stadt verbindet. Diese übereinstimmenden Datensätze ermöglichen es uns, die beiden Tabellen JOIN miteinander zu verknüpfen. Mit anderen Worten, wir kennen die Stadt eines Benutzers, wenn wir den Datensatz aus der Tabelle cities der einen id Wert hat, der dem Wert in users.city_id entspricht. In der folgenden Abfrage können wir dies in Aktion sehen: SELECT cities.*, users.* FROM cities JOIN users ON cities.id = users.city_id; cities  users cityname id city_id id first_name last_name age Miami 1 1 1 John Doe 22 Miami 1 1 2 Albert Thomson 15 Orlando 2 2 3 Robert Ford 65 Las Vegas 3 3 4 Samantha Simpson 9 Orlando 2 2 5 Carlos Bennet 42 Orlando 2 2 6 Mirtha Lebrand 81 Las Vegas 3 3 7 Alex Gomez 31 Da die users Tabelle über den Fremdschlüssel city_id mit einer Stadt verbunden ist, können wir sagen, dass ein Benutzer zu einer Stadt gehört und die Stadt somit viele Benutzer hat. Dies ist eine Eltern-Kind-Beziehung (Städte-Benutzer); die users Tabelle teilt eine Verbindung zur cities Tabelle. Mit dieser Beziehung im Hinterkopf wollen wir nun sehen, wie wir einige interessante zusammengefasste Daten berechnen können, die beide Tabellen miteinander verbinden. Aggregat + GROUP BY + JOIN Lassen Sie uns nun einige praktische Situationen betrachten, in denen wir GROUPWerte aus JOINTabellen. MIN + GRUPPE NACH + JOIN Das Berechnen von Werten auf der Grundlage von untergeordneten Datensätzen, die nach einer übergeordneten Spalte gruppiert sind, ist ziemlich üblich. Erstellen wir eine Abfrage, die den niedrigsten users.age (untergeordneten Datensatz) für jeden cityname (übergeordneten Datensatz) abruft: SELECT cities.cityname, MIN(users.age) FROM cities JOIN users ON cities.id = users.city_id GROUP BY cities.cityname Dies wird zurückgegeben: cityname MIN(users.age) Las Vegas 9 Miami 15 Orlando 42 Es gibt etwas sehr Wichtiges über die Art und Weise, wie JOIN funktioniert, zu erwähnen. Es wird deutlicher, wenn wir uns alle Städte ansehen: SELECT cities.cityname FROM cities cityname Coyote Springs Las Vegas Miami Orlando Wie Sie sehen können, war "Coyote Springs" vorher nicht aufgeführt, weil es keine Benutzer hat. Wenn Sie diese Stadt in den zusammengefassten Ergebnissen anzeigen lassen möchten, sollten Sie stattdessen eine LEFT JOIN stattdessen verwenden: SELECT cities.cityname, MIN(users.age) FROM cities LEFT JOIN users ON cities.id = users.city_id GROUP BY cities.cityname Dies wird zurückgegeben: cityname MIN(users.age) Coyote Springs null Las Vegas 9 Miami 15 Orlando 42 Ob dies sinnvoll ist oder nicht, hängt von Ihrem Anwendungsfall ab, aber es ist wichtig, dass Sie diese Situation im Hinterkopf behalten, wenn Sie Tabellen verbinden. MAX + GRUPPIEREN NACH + VERKNÜPFUNGEN Wir können das höchste Alter für jede Stadt mit der Funktion MAX() finden: SELECT cities.cityname, MAX(users.age) FROM cities LEFT JOIN users ON cities.id = users.city_id GROUP BY cities.cityname Die obige Abfrage liefert die Ergebnisse: cityname MAX(users.age) Coyote Springs null Las Vegas 31 Miami 22 Orlando 81 Beachten Sie, dass ich LEFT JOIN verwendet habe. Ich möchte eine Liste aller Städte, nicht nur derjenigen mit zugehörigen Benutzerdatensätzen. SUMME + GRUPPIERUNG NACH + VERKNÜPFUNG Schauen wir uns nun an, wie wir die Altersangaben für jede Stadt summieren können. Dazu können wir die Funktion SUM() verwenden: SELECT cities.cityname, SUM(users.age) FROM cities LEFT JOIN users ON cities.id = users.city_id GROUP BY cities.cityname Diese gibt zurück: cityname SUM(users.age) Coyote Springs null Las Vegas 40 Miami 37 Orlando 188 COUNT + GROUP BY + JOIN Angenommen, wir wollen die Anzahl der Nutzer in jeder Stadt sehen. Wir würden die Funktion COUNT() wie folgt verwenden SELECT cities.cityname, COUNT(users.id) FROM cities LEFT JOIN users ON cities.id = users.city_id GROUP BY cities.cityname Die zurückgibt: cityname COUNT(users.id) Coyote Springs 0 Las Vegas 2 Miami 2 Orlando 3 DURCHSCHNITT + GRUPPIERUNG NACH + VERKNÜPFUNG Unter Verwendung der Anzahl der Nutzer in jeder Stadt (COUNT) und der SUM des kombinierten Alters der Nutzer in jeder Stadt können wir das Durchschnittsalter für jede Stadt errechnen. Wir dividieren einfach das summierte Alter durch die Anzahl der Nutzer in jeder Stadt: SELECT cities.cityname, SUM(users.age) AS sum, COUNT(users.id) AS count, SUM(users.age) / COUNT(users.id) AS average FROM cities LEFT JOIN users ON cities.id = users.city_id GROUP BY cities.cityname Rückgabe: cityname sum count average Coyote Springs null 0 null Las Vegas 40 2 20.0000 Miami 37 2 18.5000 Orlando 188 3 62.6667 Beachten Sie, dass die Summe und der berechnete Durchschnitt einen NULL-Wert für Coyote Springs ergeben. Das liegt daran, dass Coyote Springs keine Benutzer hat und die zusammengefasste Spalte daher keinen numerischen Wert berechnen kann. AVG + GRUPPE NACH + JOINS Im vorherigen Beispiel wurde eine Berechnung verwendet, die wir eingegeben haben, um ein Durchschnittsalter für jede Stadt zu ermitteln. Wir hätten stattdessen auch die Funktion AVG() verwenden können, wie unten gezeigt: SELECT cities.cityname, AVG(users.age) FROM cities LEFT JOIN users ON cities.id = users.city_id GROUP BY cities.cityname Dies führt zu den gleichen Werten wie im vorherigen Beispiel: cityname AVG(users.age) Coyote Springs null Las Vegas 20.0000 Miami 18.5000 Orlando 62.6667 Ergebnisse filtern Manchmal müssen Sie Zeilen auf der Grundlage bestimmter Bedingungen filtern. Bei dieser Art von Abfrage gibt es drei Stufen, in denen Sie dies tun können: WHERE, HAVING, und JOIN. Je nach Situation kann jede dieser Optionen zu einem anderen Ergebnis führen. Es ist wichtig, dass Sie wissen, welche Sie verwenden müssen, wenn Sie ein bestimmtes Ergebnis wünschen. Schauen wir uns einige Beispiele an, um dies zu verdeutlichen. Verwendung des JOIN-Prädikats Ermitteln wir die Anzahl der Nutzer unter 30 Jahren in jeder Stadt. Wir verwenden LEFT JOIN um Städte ohne Benutzerdatensätze abzurufen: SELECT cityname, COUNT(users.id) FROM cities LEFT JOIN users ON cities.id = users.city_id AND users.age < 30 GROUP BY cities.cityname ORDER BY cities.cityname; Die Bedingung, nur Benutzer mit einem Alter unter 30 Jahren einzuschließen, wird im Prädikat JOIN festgelegt. Dies ergibt die folgende Ausgabe: cityname COUNT(users.id) Coyote Springs 0 Las Vegas 1 Miami 2 Orlando 0 Alle Städte werden aufgelistet, und nur die Benutzer mit einem Alter innerhalb des Bereichs geben eine Zahl ungleich Null zurück. Städte, in denen es keine Benutzer gibt, die unseren Kriterien entsprechen, geben eine Null zurück. Was wäre passiert, wenn wir dieselbe Filterbedingung in die WHERE Klausel gesetzt hätten? Verwendung von WHERE-Bedingungen Würden wir die gleichen Bedingungen in die WHERE Klausel einfügen, sähe es wie folgt aus: SELECT cityname, COUNT(users.id) FROM cities LEFT JOIN users ON cities.id = users.city_id WHERE users.age < 30 GROUP BY cities.cityname ORDER BY cities.cityname; Das Ergebnis wäre dann: cityname COUNT(users.id) Las Vegas 1 Miami 2 Das ist nicht das, was ich erwartet habe; ich wollte ALLE Städte und die Anzahl ihrer jeweiligen Nutzer unter 30 Jahren erhalten. Selbst wenn eine Stadt keine Nutzer hat, sollte sie mit einer Nullzahl aufgelistet werden, wie es das Beispiel des JOIN Prädikats zeigt. Der Grund dafür, dass diese Datensätze nicht zurückgegeben werden, ist, dass WHERE Bedingungen nach der JOIN. Da die Bedingung users.age < 30 alle "Coyote Springs"- und "Orlando"-Datensätze entfernt, kann die zusammenfassende Berechnung diese Werte nicht enthalten. Nur "Las Vegas" und "Miami" erfüllen die Bedingungen von WHERE, daher werden nur "Las Vegas" und "Miami" zurückgegeben. Im Gegensatz dazu werden bei Anwendung der Bedingung im JOIN -Prädikat Benutzerdatensätze ohne übereinstimmendes Alter entfernt , bevor die beiden Tabellen verbunden werden. Dann werden alle Städte nach Benutzerspalten abgeglichen, wie Sie es erwarten würden, wenn Sie eine LEFT JOIN. Das bedeutet, dass alle Städte in den Ergebnissen enthalten sind; nur Benutzerdatensätze, die die Bedingung users.age < 30 nicht erfüllen, werden herausgefiltert. In diesem Fall liefert das JOIN-Prädikat das gewünschte Ergebnis. HAVING-Bedingungen verwenden Wir haben dies bereits im ersten Artikel erwähnt, aber wir wiederholen es hier noch einmal: Die Verwendung der WHERE Klausel zum Filtern von zusammengefassten Spalten funktioniert nicht. Sehen Sie sich das folgende Beispiel an. SELECT cityname, COUNT(users.id) FROM cities LEFT JOIN users ON cities.id = users.city_id WHERE COUNT(users.id) > 2 GROUP BY cities.cityname ORDER BY cities.cityname; Dies führt dazu, dass die Datenbank eine Beschwerde wie diese von MySQL ausgibt: Error Code: 1111. Invalid use of group function Verwenden Sie stattdessen die HAVING Klausel: SELECT cityname, COUNT(users.id) FROM cities LEFT JOIN users ON cities.id = users.city_id GROUP BY cities.cityname HAVING COUNT(users.id) > 2 ORDER BY cities.cityname; Diese gibt die gewünschten Datensätze zurück (nur Städte mit mehr als zwei Benutzern): cityname COUNT(users.id) Orlando 3 Der Umgang mit NULLs Neben den bereits vorgestellten Randfällen ist es wichtig, etwas zu berücksichtigen, das nicht so offensichtlich ist. Kehren wir zum Beispiel von COUNT() zurück: SELECT cities.cityname, COUNT(users.id) FROM cities LEFT JOIN users ON cities.id = users.city_id GROUP BY cities.cityname Dieses gibt zurück: cityname COUNT(users.id) Coyote Springs 0 Las Vegas 2 Miami 2 Orlando 3 Hätte ich COUNT(*) anstelle von COUNT(users.id) verwendet, wäre die Gesamtzahl der Zeilen generiert worden. Dadurch hätten wir einen unbeabsichtigten Wert erhalten - in diesem Fall eine falsche "1" für "Coyote Springs". Dieses Ergebnis ist auf die Art von LEFT JOIN zurückzuführen. Hier ein Beispiel: SELECT cities.cityname, COUNT(*) FROM cities LEFT JOIN users ON cities.id = users.city_id GROUP BY cities.cityname Dies würde zurückgegeben: cityname COUNT(users.id) Coyote Springs 1 Las Vegas 2 Miami 2 Orlando 3 COUNT(*) zählt also eine "1" für Coyote Springs, weil LEFT JOIN eine Zeile mit NULL-Werten zurückgibt. Denken Sie daran, dass in COUNT(*) eine Zeile mit NULL-Werten immer noch zählt. Aus demselben Grund gibt COUNT(users.id) die erwartete Anzahl von "0" zurück; der Spaltenwert von users.id ist für Coyote Springs null. Mit anderen Worten: Verwenden Sie bei dieser Art von Abfrage immer Count(column). Ein letzter Tipp zur Arbeit mit SQL-Aggregatfunktionen Abschließend möchte ich noch hinzufügen, dass die Arbeit mit SQL-Aggregatfunktionen - insbesondere bei der Verwendung von JOIN- voraussetzt, dass Sie SQL und die Daten, mit denen Sie arbeiten, verstehen. Probieren Sie die Abfragen zunächst an einer kleineren Teilmenge Ihrer Daten aus, um sicherzustellen, dass alle Berechnungen wie erwartet funktionieren. Wenn möglich, sollten Sie einige Ausgaben mit einem Referenzwert vergleichen, um die Ergebnisse Ihrer Abfragen zu überprüfen. Denken Sie daran, dass die Verwendung von Bedingungen im Prädikat JOIN (nach ON) nicht dasselbe ist wie die Filterung in WHERE (oder die Verwendung von HAVING). Dies kann zu subtilen (oder auch nicht so subtilen) Unterschieden in Ihren zusammengefassten Daten führen, die schwer zu entdeckende Fehler zur Folge haben können. Achten Sie besonders auf die Wahl Ihrer Filterung. Wie immer danke ich Ihnen für die Lektüre und freue mich, wenn Sie Ihre eigenen Erfahrungen im Kommentarbereich mitteilen. Tags: Aggregatfunktionen JOIN