Avoir un "OU" dans une condition INNER JOIN est-il une mauvaise idée?

94

En essayant d'améliorer la vitesse d'une requête extrêmement lente (plusieurs minutes sur deux tables avec seulement ~ 50000 lignes chacune, sur SQL Server 2008 si cela compte), j'ai réduit le problème à une ORjointure interne, comme dans:

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

J'ai changé cela en (ce que j'espère est) une paire équivalente de jointures gauches, montrée ici:

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

.. et la requête s'exécute maintenant dans environ une seconde!

Est-ce généralement une mauvaise idée de mettre un ORdans une condition de jointure? Ou suis-je simplement malchanceux dans la disposition de mes tableaux?

chargé
la source
6
Montrez-nous le plan d'exécution au lieu de votre requête.
Blindy
semble être une relation étrange
nathan gonzalez
@Blindy: bonne idée. Il s'avère que les plans d'exécution montrent exactement ce que Quassnoi mentionne ci-dessous: la première requête aboutit à des boucles imbriquées, tandis que la seconde se fait avec une jointure par hachage.
chargé

Réponses:

114

Ce type de JOINn'est pas optimisable pour a HASH JOINou a MERGE JOIN.

Il peut être exprimé comme une concaténation de deux jeux de résultats:

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

, chacun d'entre eux étant une équi-jointure, cependant, SQL Serverl'optimiseur de n'est pas assez intelligent pour le voir dans la requête que vous avez écrite (bien qu'ils soient logiquement équivalents).

Quassnoi
la source
3
cela a du sens, merci. Je ne sais toujours pas s'il y a quelque chose de particulier dans ma requête, ou si je devrais simplement éviter ON w=x OR y=zcomplètement les jointures du modèle?
chargé
@ladenedge: ces jointures seront effectuées à l'aide d'un balayage de table dans une boucle imbriquée. Ceci est lent si vos tables sont grandes.
Quassnoi
pour être clair, quand vous dites «ces jointures», vous voulez dire toutes les jointures de la forme ON w=x OR y=z? (Merci pour votre patience!)
chargé
3
@ladenedge: il peut y avoir des conditions supplémentaires qui pourraient aider à SQL Servercomprendre qu'une concaténation serait nécessaire. Disons que la requête SELECT * FROM othertable WHERE parentId = 1 OR id = 2utilisera une concaténation si les deux champs sont indexés donc théoriquement rien n'empêcherait de faire la même chose dans une boucle. La SQL Serverconstruction de ce plan dépendra-t-elle de très nombreux facteurs, mais je ne l'ai jamais vu dans la vraie vie.
Quassnoi
4

J'utilise le code suivant pour obtenir un résultat différent de la condition qui a fonctionné pour moi.


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
la source
-2

Vous pouvez utiliser UNION ALL à la place.

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
la source
UNION ALLvous donnera des doublons par rapport au JOINavec une ORcondition.
CodeMonkey
Car cette UNION aura raison. Pour plus de détails, lisez le lien suivant union-instead-of-or
Mitul Panchal
1
oui mais dans votre exemple, vous l'avez écrit avec union allce qui n'est pas correct, comme le décrit également l'article vers lequel vous créez un lien.
CodeMonkey