SQL Select prendendo troppo tempo per l'esecuzione
-
16-10-2019 - |
Domanda
E 'una semplice selezionare da una tabella temporanea, sinistra unendo una tabella esistente sulla sua chiave primaria, con due sub seleziona usando la parte superiore 1 del rinvio tabella unita.
Nel codice:
SELECT
TempTable.Col1,
TempTable.Col2,
TempTable.Col3,
JoinedTable.Col1,
JoinedTable.Col2,
(
SELECT TOP 1
ThirdTable.Col1 -- Which is ThirdTable's Primary Key
FROM
ThirdTable
WHERE
ThirdTable.SomeColumn = JoinedTable.SomeColumn
) as ThirdTableColumn1,
(
SELECT TOP 1
ThirdTable.Col1 -- Which is also ThirdTable's Primary Key
FROM
ThirdTable
WHERE
ThirdTable.SomeOtherColumn = JoinedTable.SomeColumn
) as ThirdTableColumn2,
FROM
#TempTable as TempTable
LEFT JOIN
JoinedTable
ON (TempTable.PKColumn1 = JoinedTable.PKColumn1 AND
TempTable.PKColumn2 = JoinedTable.PKColumn2)
WHERE
JoinedTable.WhereColumn IN (1, 3)
Questa è una replica esatta della mia interrogazione.
Se rimuovo i due seleziona sub, funziona bene e rapidamente. Con i due seleziona sub, ricevo circa 100 record al secondo, il che è estremamente lento per questa query perché deve restituire quasi un milione di dischi.
Ho controllato per vedere se ogni tavolo ha una chiave primaria, fanno tutti. Sono tutte dotate di indici e statistiche per le loro colonne importanti, come quelle in quelli in cui clausole, e quelli nel join clausola. L'unico tavolo con nessuna chiave primaria definita né indice è la tabella temporanea, ma non è il problema sia perché non è quello relativo alle seleziona sub lento, e come ho detto, con non ha nessun problema seleziona funziona bene.
Senza quelle TOP 1
restituisce più di un risultato, e genera un errore.
Guida, chiunque?
Modifica :
Quindi, il piano di esecuzione mi ha detto che mi mancava un Indice. Ho creato, e ricreato alcuni degli altri indici. Dopo un po ', il piano di esecuzione li stava usando, e la query ora corre veloce. L'unico problema è che non sto riuscendo a fare di nuovo su un altro server, per la stessa query. Così la mia soluzione sarà quella di SUGGERIMENTO quale indice di SQL Server utilizzerà.
Soluzione
Credo che in una query milione di record, si deve evitare cose come OUTER JOINS
. Vi suggerisco di utilizzare UNION ALL
Invece di LEFT JOIN
.
Finché penso CROSS APPLY
è più efficiente di sub-query nella clausola select farò modificare la query scritta da Conard Frix, che credo sia corretto.
ora: quando ho iniziato a modificare la query ho notato che hai una clausola WHERE dicendo: JoinedTable.WhereColumn IN (1, 3)
. in questo caso, se il campo è null la condizione diventerà falsa. allora perché stai usando LEFT JOIN mentre si sta filtrando nulli righe valutati?
basta sostituire LEFT JOIN
Con INNER JOIN
, vi garantisco che diventerà più veloce.
su INDEX:
Si noti che quando si dispone di un indice in una tabella, ad esempio
table1(a int, b nvarchar)
e l'indice è:
nonclustered index ix1 on table1(a)
e si vuole fare qualcosa di simile:
select a,b from table1
where a < 10
nell'indice non avete incluso la b
colonna in modo che cosa succede?
Se SQL-server utilizza l'indice, si dovrà cercare nell'indice, denominata "Index Seek" e quindi fare riferimento alla tabella principale per ottenere colonna b
, chiamato "Look Up ". Questa procedura potrebbe richiedere molto più tempo di scansione della tabella stessa:. "Tabella di scansione"
, ma in base alle statistiche che SQL-server è, in tali situazioni, potrebbe non utilizzare l'indice a tutti.
quindi prima di tutto cercare la Execution Plan
per vedere se l'indice è usato affatto.
se sì o no entrambi, alterare l'indice per includere tutte le colonne che si sta selezionando. dire come:
nonclustered index ix1 on table1(a) include(b)
in questo caso non sarà necessario Look Up, e la query verrà eseguita in modo molto più veloce.
Altri suggerimenti
I suoi i sub seleziona nella selezione colonna che sta causando il lento ritorno. Si dovrebbe provare a utilizzare i vostri sub-select a sinistra join, o utilizzare una tabella derivata come ho definito di seguito.
Utilizzo di sinistra si unisce a due istanze di Terza tavola
SELECT
TempTable.Col1,
TempTable.Col2,
TempTable.Col3,
JoinedTable.Col1,
JoinedTable.Col2,
ThirdTable.Col1 AS ThirdTableColumn1,
ThirdTable2.Col1 AS ThirdTableColumn2
FROM #TempTable as TempTable
LEFT JOIN JoinedTable ON (TempTable.PKColumn1 = JoinedTable.PKColumn2 AND
TempTable.PKColumn 2 = JoinedTable.PKColumn2)
LEFT JOIN ThirdTable ON ThirdTable.SomeColumn = JoinedTable.SomeColumn
LEFT JOIN ThirdTable ThirdTable2 ON ThirdTable.SomeOtherColumn = JoinedTable.SomeColumn
WHERE
JoinedTable.WhereColumn IN (1, 3)
Utilizzo di una derivate tavola
SELECT
TempTable.Col1,
TempTable.Col2,
TempTable.Col3,
DerivedTable.Col1,
DerivedTable.Col2,
DerivedTable.ThirdTableColumn1,
DerivedTable.ThirdTableColumn2
FROM #TempTable as TempTable
LEFT JOIN (SELECT
JoinedTable.PKColumn2,
JoinedTable.Col1,
JoinedTable.Col2,
JoinedTable.WhereColumn,
ThirdTable.Col1 AS ThirdTableColumn1,
ThirdTable2.Col1 AS ThirdTableColumn2
FROM JoinedTable
LEFT JOIN ThirdTable ON ThirdTable.SomeColumn = JoinedTable.SomeColumn
LEFT JOIN ThirdTable ThirdTable2 ON ThirdTable.SomeOtherColumn = JoinedTable.SomeColumn)
DerivedTable ON (TempTable.PKColumn1 = DerivedTable .PKColumn2 AND
TempTable.PKColumn2 = DerivedTable.PKColumn2)
WHERE
DerivedTable.WhereColumn IN (1, 3)
Prova a croce si applicano invece
SELECT
TempTable.Col1,
TempTable.Col2,
TempTable.Col3,
JoinedTable.Col1,
JoinedTable.Col2,
ThirdTableColumn1.col1,
ThirdTableColumn2.col1
FROM
#TempTable as TempTable
LEFT JOIN
JoinedTable
ON (TempTable.PKColumn1 = JoinedTable.PKColumn2 AND
TempTable.PKColumn 2 = JoinedTablePKColumn2)
CROSS APPLY
(
SELECT TOP 1
ThirdTable.Col1 -- Which is ThirdTable's Primary Key
FROM
ThirdTable
WHERE
ThirdTable.SomeColumn = JoinedTable.SomeColumn
) as ThirdTableColumn1
CROSS APPLY (
SELECT TOP 1
ThirdTable.Col1 -- Which is also ThirdTable's Primary Key
FROM
ThirdTable
WHERE
ThirdTable.SomeOtherColumn = JoinedTable.SomeColumn
) as ThirdTableColumn2,
WHERE
JoinedTable.WhereColumn IN (1, 3)
È inoltre possibile utilizzare CTE e row_number o una query in linea con MIN
Spostare il JOIN bit dalla parte principale della clausola e metterlo come una selezione secondaria. Spostandolo a WHERE e PARTE DELLA SEZIONE garanzie che non è necessario selezionare TOP 1 più e più volte, che credo è la ragione per HTE lentezza. Se si desidera controllare questo, esaminare il piano di esecuzione.
I riferimenti ThirdTable
, (sub Seleziona nel tuo esempio), hanno bisogno la stessa attenzione indice come qualsiasi altra parte di una query.
Non importa se si utilizza sub seleziona:
(
SELECT TOP 1
ThirdTable.Col1 -- Which is ThirdTable's Primary Key
FROM
ThirdTable
WHERE
ThirdTable.SomeColumn = JoinedTable.SomeColumn
) as ThirdTableColumn1,
(
SELECT TOP 1
ThirdTable.Col1 -- Which is also ThirdTable's Primary Key
FROM
ThirdTable
WHERE
ThirdTable.SomeOtherColumn = JoinedTable.SomeColumn
) as ThirdTableColumn2,
A SINISTRA SI UNISCE (come proposto da John Hartsock):
LEFT JOIN ThirdTable ON ThirdTable.SomeColumn = JoinedTable.SomeColumn
LEFT JOIN ThirdTable ThirdTable2 ON ThirdTable.SomeOtherColumn = JoinedTable.SomeColumn
CROSS APPLY (come proposto da Conrad Frix):
CROSS APPLY
(
SELECT TOP 1
ThirdTable.Col1 -- Which is ThirdTable's Primary Key
FROM
ThirdTable
WHERE
ThirdTable.SomeColumn = JoinedTable.SomeColumn
) as ThirdTableColumn1
CROSS APPLY (
SELECT TOP 1
ThirdTable.Col1 -- Which is also ThirdTable's Primary Key
FROM
ThirdTable
WHERE
ThirdTable.SomeOtherColumn = JoinedTable.SomeColumn
) as ThirdTableColumn2
È necessario garantire covering indexes
sono definiti per ThirdTable.SomeColumn
e ThirdTable.SomeOtherColumn
e gli indici sono unici. Questo significa che sarà necessario qualificare ulteriormente i riferimenti ThirdTable
per eliminare la selezione di più righe e migliorare le prestazioni. La scelta di sub selects
, LEFT JOIN
o CROSS APPLY
non sarà davvero importa fino a migliorare la selettività per ThirdTable.SomeColumn
e ThirdTable.SomeOtherColumn
includendo più colonne al fine di garantire la selettività unica. Fino ad allora, mi aspetto le vostre prestazioni continuerà a soffrire.
Il tema covering index
è ben introdotta da Maziar Taheri; pur non ripetendo il suo lavoro, io sottolineo la necessità di prendere a cuore l'uso di indici di copertura.
In breve:
Migliorare la selettività per i ThirdTable.SomeColumn
e ThirdTable.SomeOtherColumn
query (o join) aggiungendo legati colonne tabella per assicurare una corrispondenza riga univoco. Se questo non è possibile, allora si continuerà a soffrire problemi di prestazioni come il motore è occupato tirando in righe che vengono successivamente gettati via. Ciò influisce il vostro I / O, CPU, e, in ultima analisi, il piano di esecuzione.