MSSQL Performance: JOIN + WHERE vs. WHERE + IN (SELECT)
-
Hi,
ich möchte aus einer Tabelle Werte laden, die zu bestimmten Einträgen einer verknüpften Tabelle passen. Grundsätzlich fallen mir dazu zwei Varianten ein:
SELECT [TblA].[A] FROM [TblA] INNER JOIN [TblB] ON [TblB].[B] = [TblA].[B] WHERE [TblB].[Name] = 'NameOfB'; SELECT [TblA].[A] FROM [TblA] WHERE [TblA].[B] IN (SELECT [TblB].[B] FROM [TblB] WHERE [TblB].[Name] = 'NameOfB');
Welche von diesen zwei Varianten ist denn für diesen Fall besser geeignet, beispielsweise im Hinblick auf die Performance?
Das ganze geht dann auch noch weiter, denn TblB ist wiederum mit TblC verknüpft. Dann hätte ich entweder zwei JOINs oder in dem Subselect ein weiteres Subselect. Und es gibt auch noch ne vierte Stufe...
-
Wenn es logisch ein JOIN ist, schreib es als JOIN. Wenn es logisch ein IN ist, schreib es als IN.
SQL Server wird vermutlich den gleichen Execution-Plan draus machen.(SQL Management Studio -> New Query ... Show actual execution plan -> Execute)
-
Zumindest unter Oracle war häufig ein IN langsamer als ein EXISTS, und dies würde ich daher auch noch in die Betrachtung einbringen.
-
Join wird immer auf ein Kreuzprodukt abgebildet.
-
asc schrieb:
Zumindest unter Oracle war häufig ein IN langsamer als ein EXISTS, und dies würde ich daher auch noch in die Betrachtung einbringen.
Es geht hier aber um MS-SQL. Und was der daraus macht, kann man sich wie gesagt im SQL Management Studio ansehen. Kein Grund zu raten.
BTW: ich hab' bei MS-SQL noch keinen Fall erlebt, wo so ein einfacher Unterschied (WHERE IN vs. JOIN vs. WHERE EXISTS) zu einem anderen Execution-Plan geführt hätte. (Immer vorausgesetzt die Abfragen waren 100% äquivalent formuliert natürlich). Anders gesagt: MS-SQL macht *genau* das selbe, egal wie man die Query schreibt.
Zeus schrieb:
Join wird immer auf ein Kreuzprodukt abgebildet.
Die Aussage ist, wenn es um Performane geht, unsinnig.
"Kreuzprodukt" ist eine logische Operation, die man mittels verschiedenen Algorithmen umsetzen kann.
Das Ergebnis eines Nested-Loop JOINs, eines Hash JOINs und eines Merge JOINs ist immer das selbe.
Die Performance wird aber je nach Tabellenstruktur/Daten drastisch unterschiedlich sein.
-
hustbaer schrieb:
Zeus schrieb:
Join wird immer auf ein Kreuzprodukt abgebildet.
Die Aussage ist, wenn es um Performane geht, unsinnig.
"Kreuzprodukt" ist eine logische Operation, die man mittels verschiedenen Algorithmen umsetzen kann.
Das Ergebnis eines Nested-Loop JOINs, eines Hash JOINs und eines Merge JOINs ist immer das selbe.
Die Performance wird aber je nach Tabellenstruktur/Daten drastisch unterschiedlich sein.Ohje du allwissender Weißer, ich entschuldige mich. Das Kreuzprodukt wird gebildet durch das Verknüpfen beider Tabellen über ihre Gesamtgröße und anschließen reduziert durch Selektion und Projektion. Während du bei Benutzung von gleich bedeutende Unterabfrage deine Relation stückweise vergrößerst. Aber wahrscheinlich erzähle ich nur Mist. Nun die Komplexität wird man durch verschiedene Implementierungen auch nicht niedrieger bekommen.
-
Wieder ne Menge gelernt....
Wenn's eh keinen Unterschied macht, werd ich wohl beim JOIN bleiben.
-
Zeus schrieb:
Das Kreuzprodukt wird gebildet durch das Verknüpfen beider Tabellen über ihre Gesamtgröße und anschließen reduziert durch Selektion und Projektion.
Logisch kann man das so ausdrücken.
Real macht es kein mir bekannter Datenbank-Server. Das Kreuzprodukt wird nie wirklich gebildet, das wäre total plem, würde viel zu lange dauern. (D.h. es wird natürlich wirklich geblidet, aber nur wenn man dem Server keine andere Wahl lässt, z.B. indem man keine "optimierbaren" Filterbedingungen angibt.)
Stattdessen analysiert der Server die in der Abfrage verwendeten Bedingungen (alles was in "ON" und "WHERE" angegeben wird), und baut damit einen "Algorithmus" (Execution-Plan, Graphen, wie auch immer man es nennen mag) der die Abfrage möglichst optimal umsetzt.
Während du bei Benutzung von gleich bedeutende Unterabfrage deine Relation stückweise vergrößerst.
Wieder: logisch gesehen stimmt das so. Wenn man sich anguckt wie ein DBMS die Abfragen umsetzt bestaht aber kein Unterschied.
Beispiel: eine "Spiel" Tabelle und eine "Score" Tabelle, wir wollen die Scores des Spiels "robotron". Die "Spiel" Tabelle verwendet surrogate Keys, d.h. wir müssen irgend was machen (JOINen oder was auch immer).
Folgende Abfragen:
SELECT [score].[score] FROM [score] WHERE [game_id] = (SELECT [game_id] FROM [game] WHERE [name] = 'robotron'); SELECT [score].[score] FROM [score] WHERE [game_id] IN (SELECT [game_id] FROM [game] WHERE [name] = 'robotron'); SELECT [score].[score] FROM [score] WHERE EXISTS (SELECT * FROM [game] WHERE [game].[game_id] = [score].[game_id] AND [game].[name] = 'robotron'); SELECT [score].[score] FROM [score] CROSS JOIN [game] WHERE [score].[game_id] = [game].[game_id] AND [game].[name] = 'robotron'; SELECT [score].[score] FROM [score] INNER JOIN [game] ON [score].[game_id] = [game].[game_id] WHERE [game].[name] = 'robotron';
Wenn man von einer "Assertion" absieht, die im Execution-Plan für die erste Variante vorkommt (bei "= subselect" darf "subselect" ja nur einen einzigen Wert als Ergebnis ausspucken, also nur eine Zeile), sind die Execution-Plans für alle 5 Varianten exakt gleich!
Konkret, mit der DB die ich hier habe:
Es wird ein Nested-Loops-Join verwendet.Am "oberen" ("äusseren") Eingang des Nested-Loop Operators hängt ein Table-Scan auf [game] ([game] hat keinen passenden Index, daher Table-Scan), und am "unteren" ("inneren") Eingang hängt ein Clustered-Index-Seek auf [score].
Aber wahrscheinlich erzähle ich nur Mist.
Entweder das, oder ich habe nicht verstanden worauf du hinaus willst.