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?


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).


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)

-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


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
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.