Domanda

Voglio selezionare le righe in una tabella in cui la chiave primaria è in un'altra tabella. Non sono sicuro se dovrei utilizzare un JOIN o l'operatore IN in SQL Server 2005. Esistono differenze significative nelle prestazioni tra queste due query SQL con un set di dati di grandi dimensioni (ovvero milioni di righe)?

SELECT *
FROM a
WHERE a.c IN (SELECT d FROM b)

SELECT a.*
FROM a JOIN b ON a.c = b.d
È stato utile?

Soluzione

Aggiornamento:

Questo articolo nel mio blog riassume sia la mia risposta che i miei commenti ad altre risposte e mostra i piani di esecuzione effettivi:


SELECT  *
FROM    a
WHERE   a.c IN (SELECT d FROM b)

SELECT  a.*
FROM    a
JOIN    b
ON      a.c = b.d

Queste query non sono equivalenti. Possono produrre risultati diversi se la tabella b non è conservata in chiave (ad es. I valori di b.d non sono univoci).

L'equivalente della prima query è il seguente:

SELECT  a.*
FROM    a
JOIN    (
        SELECT  DISTINCT d
        FROM    b
        ) bo
ON      a.c = bo.d

Se bd è UNIQUE e contrassegnato come tale (con un UNIQUE INDEX o UNIQUE CONSTRAINT ), quindi queste query sono identiche e molto probabilmente useranno piani identici, poiché SQL Server è abbastanza intelligente da tenerne conto.

SQL Server può utilizzare uno dei seguenti metodi per eseguire questa query:

  • Se esiste un indice su ac , d è UNIQUE e b è relativamente piccolo rispetto a a , la condizione viene propagata nella sottoquery e viene utilizzato il semplice INNER JOIN (con b in testa)

  • Se esiste un indice su bd e d non è UNIQUE , anche la condizione viene propagata e Viene utilizzato SINISTRA SEMI ISCRIVITI . Può essere utilizzato anche per la condizione precedente.

  • Se c'è un indice sia su bd che ac e sono grandi, viene usato MERGE SEMI JOIN

  • Se non esiste alcun indice su alcuna tabella, viene creata una tabella hash su b e viene utilizzato HASH SEMI JOIN .

Nessuno dei due metodi rivaluta ogni volta l'intera sottoquery.

Vedi questa voce nel mio blog per maggiori dettagli su come funziona:

Ci sono collegamenti per tutti i RDBMS dei big four.

Altri suggerimenti

Nessuno dei due. Utilizzare un JOIN ANSI-92:

SELECT a.*
FROM a JOIN b a.c = b.d

Tuttavia, è meglio come ESISTI

SELECT a.*
FROM a
WHERE EXISTS (SELECT * FROM b WHERE a.c = b.d)

Questo rimuove i duplicati che potrebbero essere generati da JOIN, ma funziona altrettanto velocemente se non più velocemente

L'IN viene valutato (e la selezione da b riesegui) per ogni riga in a, mentre JOIN è ottimizzato per utilizzare indici e altri trucchi di paging accurati ...

Nella maggior parte dei casi, tuttavia, l'ottimizzatore sarebbe probabilmente in grado di costruire un JOIN da una sottoquery correlata e finire comunque con lo stesso piano di esecuzione.

Modifica: si prega di leggere i commenti qui sotto per ulteriori ... discussioni sulla validità di questa risposta e la risposta effettiva alla domanda del PO. =)

Parlando per esperienza su una tabella con 49.000.000 di righe, consiglierei di ABBINARE A SINISTRA. L'utilizzo di IN o EXISTS ha richiesto 5 minuti per completare il completamento del JOIN LEFT OUTER in 1 secondo.

SELECT a.*
FROM a LEFT OUTER JOIN b ON a.c = b.d
WHERE b.d is not null -- Given b.d is a primary Key with index

In realtà nella mia query lo faccio su 9 tabelle.

A parte andare e testarlo su una grande quantità di dati di test per te, direi di usare JOINS. Ho sempre avuto prestazioni migliori usandole nella maggior parte dei casi rispetto a una sottoquery IN e hai molte più opzioni di personalizzazione per quanto riguarda come unirti, cosa è selezionato, cosa non è, ecc.

Sono query diverse con risultati diversi. Con la query IN otterrai 1 riga dalla tabella 'a' ogni volta che il predicato corrisponde. Con la query INNER JOIN otterrai a * b righe ogni volta che la condizione di join corrisponde. Quindi, con i valori in a di {1,2,3} eb di {1,2,2,3} otterrai 1,2,2,3 da JOIN e 1,2,3 da IN.

MODIFICA - Penso che potresti trovare alcune risposte qui che ti daranno un'idea sbagliata. Prova tu stesso e vedrai che questi sono tutti ottimi piani di query:

create table t1 (t1id int primary key clustered)
create table t2 (t2id int identity primary key clustered
    ,t1id int references t1(t1id)
)


insert t1 values (1)
insert t1 values (2)
insert t1 values (3)
insert t1 values (4)
insert t1 values (5)

insert t2 values (1)
insert t2 values (2)
insert t2 values (2)
insert t2 values (3)
insert t2 values (4)


select * from t1 where t1id in (select t1id from t2)
select * from t1 where exists (select 1 from t2 where t2.t1id = t1.t1id)
select t1.* from t1 join t2 on t1.t1id = t2.t1id

I primi due piani sono identici. L'ultimo piano è un ciclo nidificato, questa differenza è prevista perché, come ho detto sopra, il join ha una semantica diversa.

Da Documentazione MSDN su Fondamenti di subquery :

  

Molte istruzioni Transact-SQL che   includere sottoquery può essere   in alternativa formulato come join.   Altre domande possono essere poste solo con   sottoquery. In Transact-SQL, c'è   di solito nessuna differenza di prestazioni   tra una dichiarazione che include a   subquery e un equivalente semanticamente   versione che non lo fa. Tuttavia, in   alcuni casi in cui l'esistenza deve essere   selezionato, un join produce meglio   prestazione. Altrimenti, il nidificato   la query deve essere elaborata per ciascuno   risultato della query esterna per garantire   eliminazione di duplicati. In tale   casi, un approccio di join produrrebbe   risultati migliori.

Nell'esempio che hai fornito, la query nidificata deve essere elaborata una sola volta per ciascuno dei risultati della query esterna, quindi non dovrebbero esserci differenze di prestazioni. Il controllo dei piani di esecuzione per entrambe le query dovrebbe confermarlo.

Nota: sebbene la domanda stessa non specifichi SQL Server 2005, ho risposto con tale presupposto in base ai tag della domanda. Altri motori di database (anche versioni diverse di SQL Server) potrebbero non ottimizzare allo stesso modo.

Osserva il piano di esecuzione per entrambi i tipi e trai le tue conclusioni. A meno che il numero di record restituiti dalla sottoquery in " IN " l'affermazione è molto piccola, la variante IN è quasi certamente più lenta.

Userei un join, scommettendo che sarà molto più veloce di IN. Ciò presume che siano definite delle chiavi primarie, ovviamente, permettendo così all'indicizzazione di accelerare le cose tremendamente.

Si ritiene generalmente che un join sia più efficiente della sottoquery IN; tuttavia l'ottimizzatore di SQL * Server normalmente non comporta alcuna differenza di prestazioni evidente. Anche così, probabilmente è meglio programmare usando la condizione di join per mantenere coerenti i tuoi standard. Inoltre, se i tuoi dati e il tuo codice dovessero mai essere migrati in futuro, il motore di database potrebbe non essere così indulgente (ad esempio l'uso di un join anziché di una sottoquery IN fa una grande differenza in MySql).

La teoria ti porterà così lontano solo su domande come questa. Alla fine della giornata, vorrai testare entrambe le query e vedere quale effettivamente viene eseguito più velocemente. Ho avuto casi in cui la versione JOIN ha richiesto più di un minuto e la versione IN ha impiegato meno di un secondo. Ho anche avuto casi in cui JOIN era effettivamente più veloce.

Personalmente, tendo a iniziare con la versione IN se so che non avrò bisogno di alcun campo dalla tabella delle subquery. Se inizia a funzionare lentamente, ottimizzerò. Fortunatamente, per insiemi di dati di grandi dimensioni, riscrivere la query fa una differenza così evidente che puoi semplicemente cronometrarla da Query Analyzer e sapere che stai facendo progressi.

Buona fortuna!

Sono sempre stato un sostenitore della metodologia IN. Questo link contiene i dettagli di un test condotto in PostgresSQL. http://archives.postgresql.org/pgsql-performance/2005- 02 / msg00327.php

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top