SQL si unisce a sottoquery SQL (prestazioni)?


110

Vorrei sapere se ho una query di join simile a questa:

Select E.Id,E.Name from Employee E join Dept D on E.DeptId=D.Id

e una sottoquery simile a questa -

Select E.Id,E.Name from Employee Where DeptId in (Select Id from Dept)

Quando considero le prestazioni quale delle due query sarebbe più veloce e perché ?

Inoltre c'è un momento in cui dovrei preferire uno sull'altro?

Scusa se questo è troppo banale e chiesto prima ma sono confuso al riguardo. Inoltre, sarebbe fantastico se voi ragazzi mi poteste suggerire gli strumenti che dovrei usare per misurare le prestazioni di due query. Molte grazie!


5
@ Lucero, questa domanda è contrassegnata come sql-server-2008, dove il post che hai menzionato è etichettato MySql. Puoi dedurre che le risposte saranno le stesse. L'ottimizzazione delle prestazioni viene eseguita in modo diverso sui due RDBMS.
Francois Botha

Risposte:


48

Mi aspetto che la prima query sia più veloce, principalmente perché hai un'equivalenza e un JOIN esplicito. Nella mia esperienza INè un operatore molto lento, poiché SQL normalmente lo valuta come una serie di WHEREclausole separate da "OR" ( WHERE x=Y OR x=Z OR...).

Come con TUTTE LE COSE SQL, tuttavia, il tuo chilometraggio può variare. La velocità dipenderà molto dagli indici (hai indici su entrambe le colonne ID? Questo ti aiuterà molto ...) tra le altre cose.

L'unico modo REALE per sapere con certezza del 100% quale è più veloce è attivare il monitoraggio delle prestazioni (le statistiche IO sono particolarmente utili) ed eseguirli entrambi. Assicurati di svuotare la cache tra una corsa e l'altra!


16
Ho seri dubbi su questa risposta, poiché la maggior parte dei DBMS, sicuramente SQL Server 2008 e versioni successive, traduce la singola sottoquery ID (non correlata, che significa: non fa riferimento a più colonne di query esterne) in un semi join relativamente veloce. Inoltre, come notato in precedenza in un'altra risposta, il primo join reale restituirà una riga per OGNI occorrenza dell'ID corrispondente in Reparto - questo non fa differenza per un ID univoco, ma ti darà tonnellate di duplicati altrove. Ordinarli con DISTINCT o GROUP BY sarà un altro, pesante carico di prestazioni. Controlla i piani di esecuzione in SQL Server Management Studio!
Erik Hart,

2
La clausola IN come equivalente a OR si applica agli elenchi di parametri / valori, ma non alle sottoquery, che sono per lo più trattate come join.
Erik Hart,

42

Beh, credo che sia una domanda "Vecchia ma d'oro". La risposta è, dipende!". Le performance sono un argomento così delicato che sarebbe troppo sciocco dire: "Non usare mai sottoquery, unisciti sempre". Nei seguenti collegamenti, troverai alcune best practice di base che ho trovato molto utili:

Ho un tavolo con 50000 elementi, il risultato che cercavo era 739 elementi.

La mia domanda all'inizio era questa:

SELECT  p.id,
    p.fixedId,
    p.azienda_id,
    p.categoria_id,
    p.linea,
    p.tipo,
    p.nome
FROM prodotto p
WHERE p.azienda_id = 2699 AND p.anno = (
    SELECT MAX(p2.anno) 
    FROM prodotto p2 
    WHERE p2.fixedId = p.fixedId 
)

e ci sono voluti 7,9 secondi per l'esecuzione.

Alla fine la mia domanda è questa:

SELECT  p.id,
    p.fixedId,
    p.azienda_id,
    p.categoria_id,
    p.linea,
    p.tipo,
    p.nome
FROM prodotto p
WHERE p.azienda_id = 2699 AND (p.fixedId, p.anno) IN
(
    SELECT p2.fixedId, MAX(p2.anno)
    FROM prodotto p2
    WHERE p.azienda_id = p2.azienda_id
    GROUP BY p2.fixedId
)

e ci sono voluti 0,0256 secondi

Buon SQL, bene.


3
Interessante, potresti spiegare in che modo l'aggiunta di GROUP BY ha risolto il problema?
cozos

6
La tabella temporanea generata dalla sottoquery era più piccola. Pertanto l'esecuzione è più rapida poiché ci sono meno dati per il check-in.
Sirmyself

2
Penso che nella prima query tu abbia condiviso la variabile tra query esterna e sottoquery, quindi per ogni riga nella query principale, la sottoquery viene eseguita ma nella seconda la sottoquery viene eseguita solo una volta e in questo modo le prestazioni sono migliorate.
Ali Faradjpour

1
Il server SQL e MySql e ... Sql (eccetto NoSql) sono così simili nell'infrastruttura. Abbiamo una sorta di motore di ottimizzazione delle query sotto il quale converte le clausole IN (...) in join (se possibile). Ma quando hai un Group by su una colonna ben indicizzata (in base alla sua cardinalità), sarà molto più veloce. Quindi dipende davvero dalla situazione.
Alix

10

Inizia a guardare i piani di esecuzione per vedere le differenze nel modo in cui SQl Server li interpreterà. È inoltre possibile utilizzare Profiler per eseguire effettivamente le query più volte e ottenere la differenza.

Non mi aspetto che queste siano così orribilmente diverse, dove puoi ottenere reali, grandi guadagni in termini di prestazioni utilizzando i join invece delle sottoquery è quando usi le sottoquery correlate.

EXISTS è spesso migliore di uno di questi due e quando parli di left join dove vuoi che tutti i record non siano nella tabella di left join, allora NOT EXISTS è spesso una scelta molto migliore.


9

Le prestazioni si basano sulla quantità di dati su cui stai eseguendo ...

Se sono meno dati intorno a 20k. JOIN funziona meglio.

Se i dati sono più simili a 100k +, IN funziona meglio.

Se non hai bisogno dei dati dell'altra tabella, IN va bene, ma è sempre meglio scegliere EXISTS.

Tutti questi criteri ho testato e le tabelle hanno indici adeguati.


4

La performance dovrebbe essere la stessa; è molto più importante avere gli indici e il clustering corretti applicati alle tabelle (esistono alcune buone risorse su questo argomento).

(Modificato per riflettere la domanda aggiornata)


4

Le due query potrebbero non essere semanticamente equivalenti. Se un dipendente lavora per più di un dipartimento (possibile nell'azienda per cui lavoro; certamente, ciò implicherebbe che la tua tabella non è completamente normalizzata), la prima query restituirebbe righe duplicate mentre la seconda query no. Per rendere le query equivalenti in questo caso, la DISTINCTparola chiave dovrebbe essere aggiunta al fileSELECT clausola, il che potrebbe avere un impatto sulle prestazioni.

Nota che esiste una regola pratica di progettazione che afferma che una tabella dovrebbe modellare un'entità / classe o una relazione tra entità / classi ma non entrambe. Pertanto, ti suggerisco di creare una terza tabella, diciamo OrgChart, per modellare la relazione tra dipendenti e dipartimenti.


4

So che questo è un vecchio post, ma penso che questo sia un argomento molto importante, soprattutto oggigiorno dove abbiamo 10 milioni di record e parliamo di terabyte di dati.

Approfitterò anche delle seguenti osservazioni. Ho circa 45 milioni di record nella mia tabella ([data]) e circa 300 record nella mia tabella [cats]. Ho un'ampia indicizzazione per tutte le query di cui sto per parlare.

Considera l'esempio 1:

UPDATE d set category = c.categoryname
FROM [data] d
JOIN [cats] c on c.id = d.catid

rispetto all'esempio 2:

UPDATE d set category = (SELECT TOP(1) c.categoryname FROM [cats] c where c.id = d.catid)
FROM [data] d

L'esempio 1 ha impiegato circa 23 minuti per essere eseguito. L'esempio 2 ha richiesto circa 5 minuti.

Quindi concluderei che la sottoquery in questo caso è molto più veloce. Ovviamente tieni presente che sto usando unità SSD M.2 in grado di i / o @ 1 GB / sec (cioè byte non bit), quindi anche i miei indici sono molto veloci. Quindi questo potrebbe influenzare anche le velocità nella tua circostanza

Se è una pulizia dei dati una tantum, probabilmente è meglio lasciarla in esecuzione e finire. Uso TOP (10000) e vedo quanto tempo ci vuole e moltiplico per numero di record prima di raggiungere la grande query.

Se si stanno ottimizzando i database di produzione, suggerirei caldamente di pre-elaborare i dati, ovvero utilizzare trigger o job-broker per asincronizzare i record di aggiornamento, in modo che l'accesso in tempo reale recuperi i dati statici.


0

Puoi utilizzare un piano di spiegazione per ottenere una risposta obiettiva.

Per il tuo problema, un filtro Exists sarebbe probabilmente il più veloce.


2
"un filtro Exists sarebbe probabilmente il più veloce" - probabilmente no, penso, anche se una risposta definitiva richiederebbe un test rispetto ai dati effettivi. È probabile che i filtri esistenti siano più veloci in presenza di più righe con gli stessi valori di ricerca, quindi un filtro esistente potrebbe essere eseguito più velocemente se la query stava verificando se altri dipendenti erano stati registrati dallo stesso reparto, ma probabilmente non quando si confronta con un reparto tavolo.

Funzionerebbe più lentamente nell'ultimo scenario?
Snekse

Dipenderà dall'ottimizzatore: in determinate circostanze potrebbe, ma normalmente mi aspetterei prestazioni molto simili.
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.