PHP Blogger

Startseite Schreib mir ne Mail! RSS Abo Webnews

Die Macht von INNER JOINS

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…

Teile und genieße Diese Icons verzweigen auf soziale Netzwerke bei denen Nutzer neue Inhalte finden und mit anderen teilen können.
  • MisterWong
  • del.icio.us
  • Technorati
  • Digg
  • Slashdot
  • YahooMyWeb
  • Furl
  • Ma.gnolia
  • Spurl
  • Netscape
  • StumbleUpon
  • MyShare
  • blogmarks

Schreib Deine Meinung