2009-07-24 15 views
15

Come esempio, voglio ottenere l'elenco di tutti gli elementi con determinati tag applicati a loro. Ho potuto fare una delle seguenti operazioni:SQL Efficiency: WHERE IN subquery vs. JOIN quindi GROUP

SELECT Item.ID, Item.Name 
FROM Item 
WHERE Item.ID IN (
    SELECT ItemTag.ItemID 
    FROM ItemTag 
    WHERE ItemTag.TagID = 57 OR ItemTag.TagID = 55) 

O

SELECT Item.ID, Item.Name 
FROM Item 
LEFT JOIN ItemTag ON ItemTag.ItemID = Item.ID 
WHERE ItemTag.TagID = 57 OR ItemTag.TagID = 55 
GROUP BY Item.ID, Item.Name 

O qualcosa di completamente diverso.

In generale (supponendo che ci sia una regola generale), qual è un approccio più efficiente?

+0

@Larsenal: è possibile sostituire un 'SINISTRA CONGIUNTO' con un' INNER JOIN' nella seconda query, i risultati saranno gli stessi. Un 'LEFT JOIN' restituirà' NULL's per le righe in 'ItemTag' che non hanno un corrispondente' Item.ID', e la condizione 'WHERE' le filtrerà. – Quassnoi

risposta

15
SELECT Item.ID, Item.Name 
FROM Item 
WHERE Item.ID IN (
    SELECT ItemTag.ItemID 
    FROM ItemTag 
    WHERE ItemTag.TagID = 57 OR ItemTag.TagID = 55) 

o

SELECT Item.ID, Item.Name 
FROM Item 
LEFT JOIN ItemTag ON ItemTag.ItemID = Item.ID 
WHERE ItemTag.TagID = 57 OR ItemTag.TagID = 55 
GROUP BY Item.ID 

vostra seconda query non si compila, in quanto fa riferimento Item.Name senza né raggruppamento o aggregando su di esso.

Se togliamo GROUP BY dalla query:

SELECT Item.ID, Item.Name 
FROM Item 
JOIN ItemTag 
ON  ItemTag.ItemID = Item.ID 
WHERE ItemTag.TagID = 57 OR ItemTag.TagID = 55 

questi sono ancora query diverse, a meno che non ItemTag.ItemId è una chiave UNIQUE e contrassegnati come tali.

SQL Server è in grado di rilevare una condizione IN su una colonna UNIQUE, e sarà solo trasformare la condizione IN in un JOIN.

Se ItemTag.ItemID non è UNIQUE, la prima query utilizzerà una sorta di un SEMI JOIN algoritmo, che sono abbastanza efficienti in SQL Server.

È possibile trasformare la seconda query in un JOIN:

SELECT Item.ID, Item.Name 
FROM Item 
JOIN (
     SELECT DISTINCT ItemID 
     FROMT ItemTag 
     WHERE ItemTag.TagID = 57 OR ItemTag.TagID = 55 
     ) tags 
ON  tags.ItemID = Item.ID 

ma questo è un po 'meno efficiente rispetto IN o EXISTS.

veda questo articolo nel mio blog per un confronto più dettagliato delle prestazioni:

4

Penso che dipenderebbe da come l'ottimizzatore li gestisce, potrebbe anche accadere che si ottenga la stessa prestazione. Visualizza il piano di esecuzione è tuo amico qui.

1

È praticamente impossibile (a meno che tu non sia uno di quegli stupidi DBA dei guru) per dire cosa sarà veloce e cosa no senza guardare il piano di esecuzione e/o eseguire alcuni test di stress.

+2

In effetti, è facile dire: il secondo è molto più veloce. Rifiuterà semplicemente di compilare in un nanosecondo o giù di lì. – Quassnoi

+0

Penso di averlo risolto ora. – Larsenal

+2

@Quassnoi Non lo renderebbe più lento? Ci vuole una quantità infinita di tempo per restituire il risultato ... – Kasapo

2
SELECT Item.ID, Item.Name 
... 
GROUP BY Item.ID 

Questo non è T-SQL valida. Item.Name deve apparire nel gruppo per clausola o all'interno di una funzione di aggregazione, ad esempio SUM o MAX.

+0

Grazie. L'ho risolto – Larsenal

0

periodo questo:

SET SHOWPLAN_ALL ON 

quindi eseguire ogni versione della query

si può vedere se ritornano lo stesso piano, e se non guardare il TotalSubtreeCost sulla prima riga di ogni e vedere quanto sono diversi.

0

prestazioni sembra sempre di ottenere il voto, ma anche sentire "che è più conveniente acquistare hardware rispetto programmatori "

Il secondo vince sulla prestazione.

A volte è bello guardare SQL e conoscere lo scopo, ma è a questo che servono i commenti. La prima query utilizza l'altra tabella per un filtro, piuttosto semplice.

Il secondo avrebbe più senso (da uno scopo di comprensione e non di prestazione) utilizzando distinti anziché di gruppo per. Mi aspetterei che alcuni aggregati siano nella selezione, ma non ce ne sono. La velocità uccide.

0

Il secondo è più efficiente in MySQL. MySQL eseguirà di nuovo la query all'interno dell'istruzione IN per ogni WHERE condition test.