Das gefürchtete kartesische Produkt identifizieren und eliminieren

Kartesische Produkte liefern in der Regel keine nützlichen Informationen und führen oft zu Fehlern, die Ihrer Karriere als Datenbankentwickler schaden können. Lernen Sie, kartesische Joins zu erkennen und verbannen Sie sie für immer aus Ihren SELECT-Abfragen.

Ich werde nie eine meiner ersten SQL-Aufgaben als junger Junior-Entwickler bei der Bundesregierung vergessen. Ich musste für einen unserer Kunden einen Bericht erstellen, der ihm helfen sollte, einige Statistiken für das Büro des Ministers zu erstellen. Als die Zahlen des Berichts stark von ihren eigenen Schätzungen abwichen, beriefen sie ein Treffen mit meinem Chef und mir ein. Ich hatte keine Ahnung, wo oder dass ich überhaupt etwas falsch gemacht hatte, bis meine Chefin damit herausplatzte: „Robert, Sie haben ein kartesisches Produkt erstellt.“ Nachdem sie damit fertig war, mich vor unseren Kunden zu beschimpfen, wurde das Meeting vertagt und ich ging los, um meine Anfrage neu zu schreiben. Schließlich kam ich mit dem, wonach sie suchten, aber meine Chefin ließ mich nie im Stich und brachte es danach in jedem Meeting zur Sprache: „Robert, erinnerst du dich an die Zeit, als du ein kartesisches Produkt erstellt hast…“. Etwa sechs Monate später wechselte ich schließlich in eine andere Abteilung. Nun, es stimmt, dass mein Chef kein besonders nachsichtiger Mensch war, aber Fehler wie diese können der Karriere wirklich schaden. Heute möchte ich mit Ihnen teilen, was ich im Laufe der Jahre über kartesische Produkte gelernt habe, damit Sie sie erkennen und für immer aus Ihren SELECT-Abfragen verbannen können.

Wie man ein kartesisches Produkt erzeugt

Die folgende Abfrage extrahiert Daten aus zwei Tabellen ohne irgendeine Art von Filterung. Das Weglassen der WHERE-Klausel kann in Situationen nützlich sein, in denen Sie alle Zeilen einer Tabelle sehen wollen, aber nicht relevante Spalten neu anordnen oder ausblenden möchten:

SELECT name, gender, CONCAT('$', FORMAT(salary, 2)) AS 'Monthly Salary' FROM employees, shops; 

Das Problem hier ist, dass die Abfrage aus mehreren Tabellen selektiert. Ohne explizite Tabellen-Joins landen wir bei einer Art Standard-Join, genannt Cartesian Join (oder Cross Join). Der Name „Cross Join“ bezieht sich auf die Tatsache, dass er jede Zeile der ersten Tabelle mit jeder Zeile der zweiten Tabelle verknüpft. Mit anderen Worten: Cartesian Joins stellen die Summe der Anzahl der Spalten der Eingabetabellen plus das Produkt der Anzahl der Zeilen der Eingabetabellen dar.

Sie können in den Ergebnissen sehen, dass jede Zeile in der ersten Tabelle (Mitarbeiter) für jede Zeile in der zweiten Tabelle (Geschäfte) zurückgegeben wird. Da es drei Zeilen in der Tabelle shops gibt, produziert die Abfrage drei von jeder Zeile aus der Tabelle employees:

Das sind eine Menge Zeilen für zwei kleine Tabellen. Die Ergebnisse wachsen exponentiell, wenn mehr Zeilen und/oder Tabellen beteiligt sind. Aufgrund der Belastung der Systemressourcen durch eine solche Abfrage und der Tatsache, dass der resultierende Datensatz viel zu viele Informationen enthält, als dass der Abfrageautor das auswählen könnte, was interessant ist, werden kartesische Joins fast immer aus Versehen durchgeführt. Wie wir in meiner eigenen abschreckenden Geschichte gelernt haben, ist es gut zu wissen, wie man einen solchen erkennt, bevor Ihre Kunden oder Ihr Vorgesetzter mit Fragen zu Ihnen zurückkommt, warum es so viele doppelte Zeilen gibt. Tatsächlich ist das Vorhandensein vieler Duplikate in Kombination mit einer ungewöhnlich großen Ergebnismenge ein verräterisches Zeichen dafür, dass Sie es mit einem kartesischen Produkt zu tun haben könnten.

Wie Filterkriterien ein kartesisches Produkt maskieren können

SELECT-Anweisungen, die eine WHERE-Klausel enthalten, können ein kartesisches Produkt leicht verbergen, weil nicht alle Zeilen doppelt vorkommen werden. Hier ist eine unschuldig genug aussehende Abfrage, in der jemand vergessen hat, einen Tabellen-Join einzubauen:

SELECT name, gender, CONCAT('$', FORMAT(salary, 2)) AS 'Monthly Salary' FROM shops, employeesWHERE shops.shop = 'Zurich';

Da der Shop nicht in der Ergebnismenge erscheint, wäre es einfach, die Ausgabe als korrekt zu akzeptieren. Wir können jedoch leicht überprüfen, dass nur die ersten beiden Mitarbeiter in der Liste in Zürich arbeiten. Die anderen drei scheinen unseren Filterkriterien entgangen zu sein!

Die Ausgabe aller Spalten zeigt noch deutlicher, was los ist. Der Filter hat tatsächlich nur den Züricher Shop zurückgegeben. Ohne einen richtigen Tabellen-Join erzeugt die Abfrage jedoch einen Datensatz für jeden Mitarbeiter, egal ob er mit dem Züricher Shop verknüpft ist oder nicht. Das macht Sinn, wenn man bedenkt, dass die Mitarbeiter ohne einen Join mit keinem Shop verknüpft sind. Daher hat das Feld shop_id in der Tabelle employees nichts mit dem in der Tabelle shops zu tun. Die Abfrage lautet also: „Hol mir alle Zeilen aus der Tabelle shops, deren Name mit ‚Zürich‘ übereinstimmt, und alle Zeilen aus der Tabelle employees“:

shop_id

shop

id

shop_id_1

Geschlecht

Name

Gehalt

Zürich

m

Jon Simpson

Zürich

f

Barbara Breitenmoser

(NULL)

Zürich

f

Kirsten Ruegg

Zürich

m

Ralph Teller

Zürich

m

Peter Jonson

Gleichermaßen, das Eingrenzen der Ergebnisse aus der Tabelle „Mitarbeiter“ nur Zeilen aus dieser Tabelle, die den Kriterien entsprechen, und alle Zeilen aus der anderen Tabelle. Hier ist eine Abfrage, die die Mitarbeiter nach dem Gehalt filtert:

SELECT name, gender, CONCAT('$', FORMAT(salary, 2)) AS 'Monthly Salary' FROM shops, employeesWHERE employees.salary > 5500;

Sie passt auf eine Zeile in der Tabelle „Mitarbeiter“, die für jede Zeile der Tabelle „Geschäfte“ einmal angezeigt wird:

Name

Geschlecht

Monatsgehalt

Kirsten Ruegg

f

$5,600.00

Kirsten Ruegg

f

$5.600.00

Kirsten Ruegg

f

$5.600.00

Auch hier bestätigt die Einbeziehung aller Spalten diese Annahme:

id

shop_id

Geschlecht

Name

Gehalt

shop_id_1

Shop

f

Kirsten Ruegg

Zürich

f

Kirsten Ruegg

New York

f

Kirsten Ruegg

London

Nun erscheinen diese Ergebnisse nicht sofort verdächtig, denn, abhängig von den Kriterien und davon, welche Spalten angezeigt werden, sind doppelte Werte keine Seltenheit:

Die Abfrage zeigt zwar immer noch für jede Zeile in der Tabelle „Shops“ einen Mitarbeiter an, aber dieses Mal zeigt sie für jede Zeile der Tabelle „Shops“ einen Mitarbeiter an sowie drei Zeilen für den einen passenden Mitarbeiter. Mit anderen Worten, alle fünf Angestellten werden für die übereinstimmenden Läden angezeigt (der eine in Zürich) und der Angestellten-Datensatz, dessen Einkommen 5500 $ übersteigt (Kristen Ruegg), wird für jede Zeile in der Tabelle „Läden“ wiederholt.

Wie wir heute gesehen haben, liefern kartesische Produkte in der Regel keine nützlichen Informationen. Daher lautet die Moral von der Geschicht: Vermeiden Sie kartesische Joins um jeden Preis, es sei denn, Sie haben einen kristallklaren Grund dafür.

Mit SQL-Joins die richtigen Daten ermitteln

“ Alle Artikel von Kolumnist Rob Gravelle ansehen

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.