Nudge am 13.09.2008

Bug in MySQL bei LEFT JOIN

in Linux, MySQL | Tags: Bug, Left Join, MSSQL, MySQL, NULL

Also da habe ich echt gesucht, was der Fehler in meinem SQL-Statement war, bis sich herausstellte, dass es sich um einen waschechten MySQL-Bug in Version 5.0.24a handelte. Aber keine Sorge, dieser wurde bereits erkannt und wohl spätestens in Version 5.0.32 behoben. Gefahr erkannt – Gefahr gebannt. 🙂

Worum gings denn da?

Hier und da muss man bezüglich Performance und Speicherbedarf ja Kompromisse machen. Denn mit normalisierten Tabellen erreicht man zwar oft einen kleineren Speicherbedarf, muss aber bei Selects oft auf Verknüpfungen von Tabellen zurückgreifen, und die sind bekanntlich teueres Gut.

Bei Verknüpfungen über einen INNER JOIN werden alle Kombinationsmöglichkeiten kreuzweise ausgegeben, was oft zu einer Multiplikation der Zeilenanzahl beider Ausgangstabellen führt. Durch LEFT JOIN bzw. RIGHT JOIN erreicht man, dass jeder Zeile der linken bzw. rechten Tabelle nur ein Datensatz der jeweils anderen zugewiesen wird. Diese Verknüpfungen nennen sich OUTER JOIN und sind etwas langsamer. Wenn man sehr gepflegte Verweise (FOREIGN KEY) in den Tabellen hat, arbeitete MySQL in diesem Fall auch völlig korrekt.

Wenn jedoch der Verknüpfungsschlüssel NULL ist, dann sollte eigentlich kein Satz kombiniert werden. Noch schlimmer: Wenn es mehrere Ziel-Sätze mit einem NULL-Wert gab, so wurde hier wieder kreuzweise kombiniert – das stellt die Regel der Eindeutigkeit der Ausgangstabelle völlig auf den Kopf!

Wie kann des sowas passieren – NULL-Werte in Keys ?!

Tja, das geht leichter als gedacht. 🙂 Ein Beispiel: Man hat eine Tabelle mit Häusern CREATE TABLE house (id INT, name varchar(31), oldhouse INT), die man neu gebaut hat, aber manche waren schon vorher da und wurden übernommen. Diese kriegen einen oldHouse-Wert. Dann legt man sich ein paar Personen an: CREATE TABLE person (id INT, name varchar(31), house INT, oldhouse INT) und dort sind alle Bewohner und ihre Häuser gespeichert.

Jetzt entsteht folgende Situation: Nicht alle neuen Häuser haben alte IDs – klar. Die wurden einfach später gebaut. Und: Wenn neue Personen hinzukommen, haben diese nicht mehr den alten Haus-Schlüssel. So was passiert gern mal bei Daten-Migrationen, wenn schrittweise migriert oder die alten Verknüpfungen nochmals aufgelöst werden sollen.

Achtung: Wenn man jetzt nach Personen und ihren alten Häusern sucht, dann tritt der Bug zutage, denn dann werden alle neuen Personen genau so oft im Ergebnis genannt, wie es neue Häuser gibt! SELECT * FROM person p LEFT JOIN house h ON h.oldhouse=p.oldhouse;

Um dies zu vermeiden, könnte man die JOIN-Syntax auch aufbohren: SELECT * FROM person p LEFT JOIN house h ON h.oldhouse=p.oldhouse AND h.oldhouse>0 GROUP BY p.id; Dabei schluckt MySQL die zweite AND-Klausel im ON-Block, während andere Datenbank solche Gedankengänge glattweg verbieten (zum Beispiel der MS SQL Server). MySQL schluckt in diesem Fall auch die GROUP-Klausel, ohne explizit nach Aggregatfunktionen für alle anderen auszugebenden Werte zu verlangen. MSSQL ist in diesem Fall ebenso streng wie zuvor. Theoretisch bräuchte man auch nur eine der beiden Methoden anzuwenden, aber in der Praxis hatte ich ohne GROUP-Statement trotzdem Probleme – allerdings habe ich das Ergebnis nicht näher untersucht.

Fazit: Ich muss wohl irgendwann den gesamten Intranet-Server updaten! Aua! Hätte ich doch statt Ubuntu ein Standard-Debian genommen.


Das mark ich mir: Alltagz Mr Wong Yigg Del.icio.us Yahoo MyWeb Blinklist Google folkd
 

Leave a Reply

Your email address will not be published. Required fields are marked *