Avere un 'OR' in una condizione INNER JOIN è una cattiva idea?


94

Nel tentativo di migliorare la velocità di una query immensamente lenta (diversi minuti su due tabelle con solo ~ 50.000 righe ciascuna, su SQL Server 2008 se è importante), ho ristretto il problema a un ORnel mio inner join, come 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

L'ho cambiato in (quello che spero sia) una coppia equivalente di left join, mostrato qui:

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 la query ora viene eseguita in circa un secondo!

È generalmente una cattiva idea mettere una ORcondizione di join? O sono solo sfortunato in qualche modo nella disposizione dei miei tavoli?


6
Mostraci il piano di esecuzione invece della tua richiesta.
Blindy

sembra una relazione strana
nathan gonzalez

@ Blindy: buona idea. Si scopre che i piani di esecuzione mostrano proprio ciò che Quassnoi menziona di seguito: la prima query risulta in cicli annidati, mentre la seconda viene eseguita con un hash join.
ladenedge

Risposte:


114

Questo tipo di JOINnon è ottimizzabile per a HASH JOINo a MERGE JOIN.

Può essere espresso come una concatenazione di due set di risultati:

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

, essendo ognuno un equijoin, l' SQL Serverottimizzatore di s non è abbastanza intelligente da vederlo nella query che hai scritto (sebbene siano logicamente equivalenti).


3
questo ha senso, grazie. Non sono ancora sicuro se ci sia qualcosa di peculiare nella mia query o se dovrei semplicemente evitare del tutto i join del ON w=x OR y=zpattern?
ladenedge

@ladenedge: questi join verranno eseguiti utilizzando una scansione della tabella in un ciclo annidato. Questo è lento se i tuoi tavoli sono grandi.
Quassnoi

giusto per essere chiari, quando dici "questi join", intendi tutti i join della forma ON w=x OR y=z? (Grazie per la pazienza!)
ladenedge

3
@ladenedge: potrebbero esserci condizioni aggiuntive che potrebbero aiutare a SQL Servercapire che sarebbe necessaria una concatenazione. Supponiamo che la query SELECT * FROM othertable WHERE parentId = 1 OR id = 2utilizzerà una concatenazione se entrambi i campi sono indicizzati, quindi in teoria non c'è nulla che impedirebbe di fare la stessa cosa in un ciclo. Se SQL Servercostruirà questo piano effettivamente o no, dipende da molti fattori, ma non l'ho mai visto costruito nella vita reale.
Quassnoi

5

Uso il seguente codice per ottenere risultati diversi dalla condizione che ha funzionato per me.


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

Puoi invece usare 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


UNION ALLti darà duplicati rispetto a JOINcon una ORcondizione.
CodeMonkey

Per quell'UNIONE avrà ragione. Per maggiori dettagli leggi il seguente link union-instead-of-or
Mitul Panchal

1
si ma nel tuo esempio l'hai scritto con union all che non è corretto come descrive anche l'articolo a cui ti colleghi.
CodeMonkey
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.