Gerade eben wieder erlebt - INNER JOINS machen das Leben schön. Aber leider stelle ich ständig fest, das es MySQL Banausen gibt, die felsenfest behaupten, dass es keinen Unterschied zwischen CROSS JOINS, LEFT JOINS und INNER JOINS gibt.
Okay, kleines Beispiel. Eben rief mich ein Kunde an und meinte, es dauert etwa 20 Sekunden, bis eine bestimmte Seite in der Administration angezeigt wird. Zuverlässig reproduzierbar. Ein Blick auf die MySQL Konsole und schon war klar, wo der Hase begraben liegt.
Die Seite hat einen Eigenschaften-Block, der aus einem allgemeinen Daten-Repository geladen wird. Das Repository ist durch 3 Tabellen realisiert: Die erste beinhaltet die Datensatz-definitionen, die zweite die Feld-Definitionen und die dritte die Wert/Feld/Datensatz Verknüpfung. Und so viele Datensätze waren in den einzelnen Tabellen:
- Datensatz-Tabelle: 14000
- Feld-Tabelle: 300
- Werte-Tabelle: 111000
Macht bei einem LEFT JOIN etwa 466.200.000.000 (466 Milliarden) Datensätze in der temporären Tabelle des DB-Servers…
Um alle Datensätze eines Typs zu selecten und den Wert eines bestimmten Feldes zu erhalten, wurden von meinem Kollegen LEFT JOINS verwendet. Das Problem an LEFT und CROSS JOINS ist, dass zunächst jeder Datensatz mit jedem kombiniert wird und erst in der WHERE Klausel (die zuletzt ausgeführt wird) Datensätze die nicht benötigt werden, heraus gefiltern werden.
Wenn mans geschickt macht, kann man schon viel mehr Performance erreichen, in dem man Teile der WHERE-Klausel in den ON-Part beim joinen macht. Beispiel gefällig?
Schlecht:
SELECT * FROM `tabelle1` LEFT JOIN `tabelle2` ON `tabelle1`.`tab2_id` = `tabelle2`.`auto_tab2_id` WHERE `tabelle2`.`auto_tab2_id` >= 5 AND `tabelle2`.`auto_tab2_id` <= 10;
Besser:
SELECT * FROM `tabelle1` LEFT JOIN `tabelle2` ON `tabelle1`.`tab2_id` = `tabelle2`.`auto_tab2_id` AND `tabelle2`.`auto_tab2_id` >= 5 AND `tabelle2`.`auto_tab2_id` <= 10;
Das macht schon mal massig Performance aus. Bei einem JOIN von Primärschlüssel auf Fremdschlüssel (etwa wie im Fall gerade) kann man sogar noch weiter gehen und einen INNER JOIN verwenden:
Richtig gut:
SELECT * FROM `tabelle1` INNER JOIN `tabelle2` ON `tabelle1`.`tab2_id` = `tabelle2`.`auto_tab2_id` AND `tabelle2`.`auto_tab2_id` >= 5 AND `tabelle2`.`auto_tab2_id` <= 10;
Damit werden keine NULL-Datensätze erzeugt, falls es zu einem PK aus Tabelle1 keinen FK in Tabelle2 gibt. Bei meinem Problem heute mittag hat das aus einem 20 Sekunden-Statement eins gemacht, das unter einer Sekunde Ausführungszeit benötigt und nur noch etwa 300 Datensätze in der temporären Tabelle erzeugt…


















