Ter um 'OR' em uma condição INNER JOIN é uma má ideia?

94

Ao tentar melhorar a velocidade de uma consulta extremamente lenta (vários minutos em duas tabelas com apenas ~ 50.000 linhas cada, no SQL Server 2008 se for importante), reduzi o problema a um ORem minha junção interna, como em:

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

Mudei isso para (o que espero que seja) um par equivalente de junções à esquerda, mostrado aqui:

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

.. e a consulta agora é executada em cerca de um segundo!

Geralmente é uma má ideia colocar um ORem uma condição de junção? Ou estou apenas com azar de alguma forma no layout das minhas mesas?

carregado
fonte
6
Mostre-nos o plano de execução em vez de sua consulta.
Blindy
parece uma relação estranha
nathan gonzalez
@Blindy: boa ideia. Acontece que os planos de execução mostram exatamente o que Quassnoi menciona a seguir: a primeira consulta resulta em loops aninhados, enquanto a segunda é feita com uma junção de hash.
carregado

Respostas:

114

Este tipo de JOINnão é otimizado para a HASH JOINou a MERGE JOIN.

Pode ser expresso como uma concatenação de dois 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

, cada um deles sendo um equijoin, no entanto, SQL Servero otimizador de não é inteligente o suficiente para ver isso na consulta que você escreveu (embora eles sejam logicamente equivalentes).

Quassnoi
fonte
3
isso faz sentido, obrigado. Ainda não tenho certeza se há algo peculiar em minha consulta ou se devo apenas evitar ON w=x OR y=ztotalmente as junções do padrão.
carregado
@ladenedge: essas junções serão realizadas usando uma varredura de tabela em um loop aninhado. Isso é lento se suas tabelas forem grandes.
Quassnoi
só para ficar claro, quando você diz "essas junções", quer dizer todas as junções do formulário ON w=x OR y=z? (Obrigado por sua paciência!)
ladenedge
3
@ladenedge: pode haver condições adicionais que podem ajudar a SQL Serverentender que uma concatenação seria necessária. Digamos que a consulta SELECT * FROM othertable WHERE parentId = 1 OR id = 2usará uma concatenação se os dois campos forem indexados, portanto, teoricamente, não há nada que impeça de fazer a mesma coisa em um loop. Se SQL Servervai construir esse plano de verdade ou não, depende de muitos fatores, mas nunca vi isso ser construído na vida real.
Quassnoi
4

Eu uso o seguinte código para obter resultados diferentes da condição que funcionou para mim.


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

Você pode usar UNION ALL em seu lugar.

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
fonte
UNION ALLfornecerá duplicatas em comparação com JOINcom uma ORcondição.
CodeMonkey
Pois essa UNIÃO estará certa. Para mais detalhes, leia o seguinte link união-em-vez-ou-ou
Mitul Panchal
1
sim, mas no seu exemplo você escreveu com o union allque não é correto, como o artigo para o qual o link também descreve.
CodeMonkey