¿Tener un 'O' en una condición INNER JOIN es una mala idea?

94

Al tratar de mejorar la velocidad de una consulta inmensamente lenta (varios minutos en dos tablas con solo ~ 50,000 filas cada una, en SQL Server 2008 si es importante), reduje el problema a una ORcombinación interna, como en:

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

Cambié esto a (lo que espero sea) un par equivalente de uniones izquierdas, que se muestran aquí:

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

.. y la consulta ahora se ejecuta en aproximadamente un segundo.

¿Es generalmente una mala idea poner un ORen una condición de unión? ¿O simplemente tengo mala suerte en el diseño de mis tablas?

ladenedge
fuente
6
Muéstranos el plan de ejecución en lugar de tu consulta.
Blindy
parece una relación extraña
nathan gonzalez
@Blindy: buena idea. Resulta que los planes de ejecución muestran exactamente lo que Quassnoi menciona a continuación: la primera consulta da como resultado bucles anidados, mientras que la segunda se realiza con una combinación hash.
ladenedge

Respuestas:

114

Este tipo de JOINno es optimizable para a HASH JOINo a MERGE JOIN.

Puede expresarse como una concatenación de dos conjuntos de resultados:

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

, siendo cada uno de ellos un equijoin, sin embargo, SQL Serverel optimizador no es lo suficientemente inteligente como para verlo en la consulta que escribió (aunque son lógicamente equivalentes).

Quassnoi
fuente
3
esto tiene sentido, gracias. Todavía no estoy seguro de si hay algo peculiar en mi consulta, o si debo evitar las uniones del ON w=x OR y=zpatrón por completo.
ladenedge
@ladenedge: estas uniones se realizarán mediante un escaneo de tabla en un bucle anidado. Esto es lento si sus mesas son grandes.
Quassnoi
para que quede claro, cuando dice "estas uniones", ¿se refiere a todas las uniones del formulario ON w=x OR y=z? (¡Gracias por tu paciencia!)
ladenedge
3
@ladenedge: puede haber condiciones adicionales que podrían ayudar a SQL Servercomprender que se necesitaría una concatenación. Digamos que la consulta SELECT * FROM othertable WHERE parentId = 1 OR id = 2usará una concatenación si ambos campos están indexados, por lo que, en teoría, no hay nada que impida hacer lo mismo en un bucle. Si SQL Serverconstruiremos este plan o no, depende de muchos factores, pero nunca lo he visto construido en la vida real.
Quassnoi
5

Utilizo el siguiente código para obtener un resultado diferente de la condición que funcionó para mí.


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
fuente
-2

En su lugar, puede utilizar UNION ALL.

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
fuente
UNION ALLle dará duplicados en comparación con JOINcon una ORcondición.
CodeMonkey
Porque esa UNIÓN tendrá razón. Para obtener más detalles, lea el siguiente enlace unión-en-lugar-de-o
Mitul Panchal
1
sí, pero en su ejemplo lo escribió con lo union allque no es correcto como también describe el artículo al que enlaza.
CodeMonkey