Wie ich lernte in einer MySQL-Datenbank mittels einem SELECT und LEFT JOINs richtig zu zählen (COUNT).
Vorgestern war’s, da bastelte ich wieder an einer Funktion für eine Webapplikation. Die Aufgabe war eine Anzeige der Anzahl von Datensätzen in 3 verknüpften Tabellen, welche in Relation zu einer Haupttabelle stehen.Das gestaltete sich recht schwierig und meine Recherchen zu dem Thema dauerten gewiß eine gute Stunde. Aber letzlich gelang es mir doch, drei SELECTs in eines zu packen.
Ich habe eine Haupttabelle, welche als Primary eine LfdNr hat. Nennen wir sie “User”. Ganz einfach.
Dann habe ich eine weitere Tabelle, die wir einfach mal Bücher nennen. Hier sind für jeden User Bücher hinterlegt. Dazu gibt es als Primary die UserId sowie eine Büchernummer. Jede Kombination als User und Büchernummer ist unique (eindeutig).
Eine dritte Tabelle soll DVD heißen und für jeden User mehrere DVDs speichern. Analog zur Tabelle mit den Büchern gibt es auch hier einen Primary aus UserId und DVDNummer.
Und als letztes beinhaltet eine vierte Tabelle zu jedem User CDs. Das Prinzip ist das Gleiche wie bei den anderen beiden Tabellen: Primary = UserId + CDNummer.
Ich wollte nun eine Tabelle ausgeben, welche die Namen der User sowie die Anzahl der Bücher, DVDs und CDs enthalten, die für den jeweiligen User in der MySQL-Datenbank gespeichert sind.
Ganz einfach könnte ich nun hergehen und zunächst die User per SELECT lesen. Dann gehe ich die Ergebnisliste des SELECT per mysql_fetch_array durch, und bastel mir die entsprechenden SELECTs für die Bücher und DVDs und CDs zusammen. Die könnte ich dann sequentiell ausgeben.
Ich wollte das aber in einem SELECT erschlagen.
Meine erste Idee sah so aus:
SELECT a .Name, a.Vorname,
IFNULL( COUNT( b.LfdNrUser ) , ‘0’ ) AS AnzahlBuecher,
IFNULL( COUNT( c.UserId) , ‘0’ ) AS AnzahlDVDs,
IFNULL( COUNT( d.UserId) , ‘0’ ) AS AnzahlCDs
FROM user AS a
LEFT JOIN buecher AS b ON a.LfdNr = b.UserId
LEFT JOIN dvds AS c ON a.LfdNr = c.UserId
LEFT JOIN cds AS d ON a.LfdNr = d.UserId
GROUP BY a.LfdNr
Funktioniert ganz wunderbar – mit einem “Schönheitsfehler”: die COUNTs vervielfachen sich auf mystische Art und Weise. Die Anzahl der DVDs und CDs werden multipliziert. Ich habe das Statement in abgewandelter Form irgendwo im Netz gefunden und ausprobiert. Bei meinem Test stimmten die Ergebnisse aber nicht, sobald für einen User Daten in mind. zwei verknüpften Tabellen gefunden wurden.
Und zunächst habe ich das auch ums Verrecken nicht gebacken bekommen.
Bis ich dann das SQL-SELECT ein wenig modifizierte. Hier ist die Lösung:
SELECT a .Name, a.Vorname,
IFNULL( COUNT( DISTINCT(b.Buechernummer)) , ‘0’ ) AS AnzahlBuecher,
IFNULL( COUNT( DISTINCT(c.DVDNummer)), ‘0’ ) AS AnzahlDVDs,
IFNULL( COUNT( DISTINCT(d.CDNummer)) , ‘0’ ) AS AnzahlCDs
FROM user AS a
LEFT JOIN buecher AS b ON a.LfdNr = b.UserId
LEFT JOIN dvds AS c ON a.LfdNr = c.UserId
LEFT JOIN cds AS d ON a.LfdNr = d.UserId
GROUP BY a.LfdNr
Die Unterschiede zum ersten Versuch sind fett dargestellt. Ich zähle nicht die UserID, sondern die jeweiligen zweiten Teile der Primaries. Dazu noch mit einem DISTINCT, um ja auch nur jeden Teil einmal zu erwischen.
Und siehe da – es funtkioniert tadellos.
Das Prinzip dieser LEFT JOINs ist mir dabei etwas klarer geworden. So ganz durchblicken tu ich da noch nicht. Und so kann es natürlich sein, dass es eine elegantere Lösung für mein Problem gibt.
Über Tipps , Erklärungen und Verbesserungsvorschläge würde ich mich freuen.
Für’s erste freue ich mich darüber, dass ich mein Problem so in den Griff bekommen habe und ich die Daten mit einem SELECT so aus der MySQL-Datenbank bekommen habe wie ich sie brauche.
Mir gehts genauso wie meinen “Vorrednern”. Das Ding habt mich einige Zeit gekostet. Hab allerdings 6 Tabellen die gezählt werden sollen. Das gab Ergebnisse! ;)
Hi Philipp,
schön, dass dir der Tipp geholfen hat. So eine Geschichte ist sonst ziemlich zeitraubend. :)
Viele Grüße vom Bodensee
Jörg – der Themenmixer
hast mir den tag gerettet damit, danke :)
das einzige was ich ändern musste war ’0’ in “0”