2015-08-07 21 views
5

Sto avendo un problema di prestazioni.Multipli INNER JOIN è troppo lento SQL SERVER

Ho creato una tabella che riceve dati da un file, faccio un BULK INSERT. Quindi eseguo un SELECT con più INNER JOIN s (11 inner joins) da inserire in un'altra tabella con i dati corretti.

Quando eseguo questo SELECT, ci vuole troppo tempo (più di un'ora) e quindi lo interrompo. La mia soluzione era quella di rompere questa query in 3, creando le tabelle @temp. Con mia sorpresa, ci vogliono 3 minuti. Questo è quello che sto cercando di capire, PERCHÉ rompere la mia domanda in 3 è stato più veloce di una dichiarazione selezionata. Qui è la mia domanda:

SELECT t1.ReturnINT, t1.ReturnBIT, t2.ReturnINT, t3.ReturnINT, t5.ReturnINT, t1.ReturnDateTime 
FROM t1 
INNER JOIN t2 
    ON t2.my_column_varchar = t1.my_column_varchar 
INNER JOIN t3 
    ON t3.my_column_number = t1.my_column_number AND t2.my_column_ID = t3.my_column_ID 
INNER JOIN t4 
    ON t4.my_column_varchar = t1.my_column_varchar 
INNER JOIN t5 
    ON t5.my_column_int = t1.my_column_int AND t5.my_column_int = t4.my_column_int AND t2.my_column_int = t5.my_column_int 
INNER JOIN t6 
    ON t6.my_column_int = t5.my_column_int AND t6.my_column_int = t2.my_column_int 
INNER JOIN t7 
    ON t7.my_column_int = t6.my_column_int 
INNER JOIN t8 
    ON t8.my_column_int = t3.my_column_int AND t8.my_column_datetime = t1.my_column_datetime 
INNER JOIN t9 
    ON t9.my_column_int = t3.my_column_int AND t8.my_column_datetime BETWEEN t9.my_column_datetime1 AND t9.datetime1 + t9.my_column_datetime2 
INNER JOIN t10 
    ON t10.my_column_int = t9.my_column_int AND t10.my_column_int = t6.my_column_int 
INNER JOIN t11 
    ON t11.my_column_int = t9.my_column_int AND t8.my_column_datetime = t11.my_column_datetime 

---- ---- A CURA

non c'è dove la clausola, la mia domanda è esattamente come ho messo qui.

Ecco le mie ricerche interrotte, ho dimenticato di metterle qui. Funziona in 3 minuti.

DECLARE @temp TABLE (
    <Some_columns> 
) 
INSERT INTO @temp 
    SELECT <My_Linked_Columns> 
    FROM t1 
    INNER JOIN t2 
     ON t2.my_column_varchar = t1.my_column_varchar 
    INNER JOIN t3 
     ON t3.my_column_number = t1.my_column_number AND t2.my_column_ID = t3.my_column_ID 
    INNER JOIN t4 
     ON t4.my_column_varchar = t1.my_column_varchar 
    INNER JOIN t5 
     ON t5.my_column_int = t1.my_column_int AND t5.my_column_int = t4.my_column_int AND t2.my_column_int = t5.my_column_int 


DECLARE @temp2 TABLE(
    <Some_Columns> 
) 
INSERT INTO @temp2 
    SELECT <More_Linked_Columns> 
    FROM @temp as temp 
    INNER JOIN t6 
     ON t6.my_column_int = temp.my_column_int AND t6.my_column_int = temp.my_column_int 
    INNER JOIN t7 
     ON t7.my_column_int = t6.my_column_int 
    INNER JOIN t8 
     ON t8.my_column_int = temp.my_column_int AND t8.my_column_datetime = temp.my_column_datetime 


DECLARE @temp3 TABLE(
    <Some_Columns> 
) 
INSERT INTO @temp3 
    SELECT <More_Linked_Columns> 
    FROM @temp2 AS temp2 
    INNER JOIN t9 
     ON t9.my_column_int = temp2.my_column_int AND temp2.my_column_datetime BETWEEN t9.my_column_datetime1 AND t9.datetime1 + t9.my_column_datetime2 
    INNER JOIN t10 
     ON t10.my_column_int = t9.my_column_int AND t10.my_column_int = temp2.my_column_int 
    INNER JOIN t11 
     ON t11.my_column_int = t9.my_column_int AND temp2.my_column_datetime = t11.my_column_datetime 


SELECT <All_Final_Columns> 
FROM @temp3 

---- ---- modificato 3

Studiando altre cose che ho scoperto un problema nel piano di esecuzione. Ho un ciclo nidificato che stima 1 riga ma in realtà restituisce righe 1.204.014. Credo che il problema è proprio qui, ma non ho trovato il modo di risolvere questo problema senza rompere la mia domanda in 3 parti (ora so perché romperlo è più veloce hehehe)

motivi
+2

Questo potrebbe essere un numero qualsiasi di cose. Potrebbe essere indicizzazione, potrebbe essere che tu abbia predicati non SAGGabili se esiste una clausola where, la lista potrebbe andare avanti e avanti. Senza alcuni dettagli reali c'è poco che nessuno può fare se non indovinare cosa sta facendo questo lento. –

+1

Riduce le scelte di Optimizer per decidere l'ordine di join e il tipo di join –

risposta

2

più comuni:

Motivo 1: Quando due tavole aventi n ed m righe partecipano INNER JOIN hanno molti a molti, allora la INNER JOIN può vicino un CROSS JOIN e può produrre set di risultati con più di MAX (n, m) righe, righe teoricamente nxm sono possibili

Ora immagina molte di queste tabelle in INNER JOIN.

In questo modo il set di risultati diventa sempre più grande e inizierà a mangiare nell'area di memoria allocata.

Questo potrebbe essere un motivo per cui i tavoli temporanei potrebbero aiutarti.

Motivo 2: Non hai INDEX costruito sulle colonne su cui stai unendo le tabelle.

Motivo 3: Avete delle funzioni nella clausola WHERE?

1

In generale, si desidera che Query Optimizer unisca le tabelle in modo tale da limitare il più possibile il set di risultati. Se hai la tabella A con 1 milione di righe, la tabella B con 1 milione di righe e la tabella C con 10 righe, prima dovresti eseguire il join interno dalla tabella C ad A o B, questo ti darebbe al massimo 10 record (assumendo una corrispondenza 1: 1) per poi unirsi all'ultima tabella. Se ti iscrivi da A a B per prima cosa, ti unirai a tutte e 1 milione di file da ognuna, il che richiederebbe un po 'più di tempo.

In genere, Query Optimizer è "abbastanza buono" per scegliere l'ordine di partecipazione, ma nel tuo caso non lo era.Il metodo migliore che ho visto per forzare l'ordine di join è stato dimostrato da Adam Mechanic in a blog post here. Implica l'uso di una clausola TOP sulle tabelle da cui vuoi iniziare il join. Query Optimizer otterrà quindi il set di risultati da tali tabelle per primo e potrai davvero limitare il numero totale di righe e aumentare le prestazioni della query. Io uso questo metodo quando possibile.

0

Con l'indice o gli indici corretti, la query originale deve essere eseguita molto rapidamente (meno di un secondo se si pagina i dati). Non utilizzare le tabelle temporanee come un trucco per non essere in grado di fornire una query ragionevole.