Ist es eine schlechte Idee, ein 'ODER' in einem INNER JOIN-Zustand zu haben?

94

Bei dem Versuch, die Geschwindigkeit einer immens langsamen Abfrage zu verbessern (mehrere Minuten in zwei Tabellen mit jeweils nur ~ 50.000 Zeilen, wenn es darauf ankommt, unter SQL Server 2008), habe ich das Problem auf einen ORin meinem inneren Join eingegrenzt, wie in:

SELECT mt.ID, mt.ParentID, ot.MasterID
  FROM dbo.MainTable AS mt
  INNER JOIN dbo.OtherTable AS ot ON ot.ParentID = mt.ID
                                  OR ot.ID = mt.ParentID

Ich habe dies in ein äquivalentes Paar linker Verknüpfungen geändert (was ich hoffe), das hier gezeigt wird:

SELECT mt.ID, mt.ParentID,
   CASE WHEN ot1.MasterID IS NOT NULL THEN
      ot1.MasterID ELSE
      ot2.MasterID END AS MasterID
  FROM dbo.MainTable AS mt
  LEFT JOIN dbo.OtherTable AS ot1 ON ot1.ParentID = mt.ID
  LEFT JOIN dbo.OtherTable AS ot2 ON ot2.ID = mt.ParentID
  WHERE ot1.MasterID IS NOT NULL OR ot2.MasterID IS NOT NULL

.. und die Abfrage läuft jetzt in ca. einer Sekunde!

Ist es im Allgemeinen eine schlechte Idee, OReine Join-Bedingung zu setzen? Oder habe ich einfach Pech beim Layout meiner Tische?

beladen
quelle
6
Zeigen Sie uns den Ausführungsplan anstelle Ihrer Anfrage.
Blindy
scheint eine seltsame Beziehung zu sein
Nathan Gonzalez
@Blindy: gute Idee. Es stellt sich heraus, dass die Ausführungspläne genau das zeigen, was Quassnoi unten erwähnt: Die erste Abfrage führt zu verschachtelten Schleifen, während die zweite mit einem Hash-Join ausgeführt wird.
Beladung

Antworten:

114

Diese Art von JOINist nicht für a HASH JOINoder a optimierbar MERGE JOIN.

Es kann als Verkettung von zwei Ergebnismengen ausgedrückt werden:

SELECT  *
FROM    maintable m
JOIN    othertable o
ON      o.parentId = m.id
UNION
SELECT  *
FROM    maintable m
JOIN    othertable o
ON      o.id = m.parentId

Da jeder von ihnen ein Equijoin ist, ist SQL Serverder Optimierer jedoch nicht intelligent genug, um ihn in der von Ihnen geschriebenen Abfrage zu sehen (obwohl sie logisch äquivalent sind).

Quassnoi
quelle
3
Das macht Sinn, danke. Ich bin mir immer noch nicht sicher, ob meine Abfrage etwas Besonderes ist oder ob ich nur Verknüpfungen des ON w=x OR y=zMusters ganz vermeiden sollte .
Beladung
@ladenedge: Diese Verknüpfungen werden mithilfe eines Tabellenscans in einer verschachtelten Schleife ausgeführt. Dies ist langsam, wenn Ihre Tabellen groß sind.
Quassnoi
Nur um klar zu sein, wenn Sie "diese Verknüpfungen" sagen, meinen Sie alle Verknüpfungen des Formulars ON w=x OR y=z? (Danke für Ihre Geduld!)
beladen
3
@ladenedge: Es kann zusätzliche Bedingungen geben, die helfen können zu SQL Serververstehen, dass eine Verkettung erforderlich wäre. Angenommen, die Abfrage SELECT * FROM othertable WHERE parentId = 1 OR id = 2verwendet eine Verkettung, wenn beide Felder indiziert sind, sodass theoretisch nichts daran hindert, dasselbe in einer Schleife zu tun. Ob SQL Serverdieser Plan tatsächlich erstellt wird oder nicht, hängt von sehr vielen Faktoren ab, aber ich habe ihn noch nie im wirklichen Leben gesehen.
Quassnoi
4

Ich verwende den folgenden Code, um ein anderes Ergebnis als die für mich funktionierende Bedingung zu erhalten.


Select A.column, B.column
FROM TABLE1 A
INNER JOIN
TABLE2 B
ON A.Id = (case when (your condition) then b.Id else (something) END)
MEO
quelle
-2

Sie können stattdessen UNION ALL verwenden.

SELECT mt.ID, mt.ParentID, ot.MasterID FROM dbo.MainTable AS mt Union ALL SELECT mt.ID, mt.ParentID, ot.MasterID FROM dbo.OtherTable AS ot

Mitul Panchal
quelle
UNION ALLSie erhalten Duplikate im Vergleich zu denen JOINmit einer ORBedingung.
CodeMonkey
Dafür wird UNION richtig sein. Für weitere Informationen lesen Sie den folgenden Link Union-statt-oder
Mitul Panchal
1
Ja, aber in Ihrem Beispiel haben Sie es geschrieben, union allwas nicht korrekt ist, wie der Artikel, auf den Sie verlinken, ebenfalls beschreibt.
CodeMonkey