SQL Select impiega troppo tempo per essere eseguito


9

È una selezione semplice da una tabella temporanea, a cui si aggiunge una tabella esistente sulla sua chiave primaria, con due sotto-selezioni che utilizzano la prima 1 che fa riferimento alla 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 domanda.

Se rimuovo i due sottoselezioni, funziona perfettamente e rapidamente. Con i due sub-selezioni, ottengo circa 100 record al secondo, il che è estremamente lento per questa query perché dovrebbe restituire quasi un milione di record.

Ho controllato per vedere se ogni tabella ha una chiave primaria, lo fanno tutti. Tutti hanno indici e statistiche per le loro colonne importanti, come quelli nelle clausole WHERE e quelli nella clausola JOIN. L'unica tabella senza chiave primaria definita né indice è la tabella temporanea, ma non è nemmeno il problema perché non è quella relativa alle selezioni secondarie lente e, come ho già detto, senza le selezioni secondarie funziona alla perfezione.

Senza quelli TOP 1restituisce più di un risultato e genera un errore.

Aiuto qualcuno?

MODIFICA :

Quindi il piano di esecuzione mi ha detto che mi mancava un indice. L'ho creato e ricreato alcuni degli altri indici. Dopo un po ', il piano di esecuzione li utilizzava e la query ora viene eseguita rapidamente. L'unico problema è che non riesco a farlo di nuovo su un altro server, per la stessa query. Quindi la mia soluzione sarà quella di SUGGERIMENTO quale indice verrà utilizzato da SQL Server.


Wow, è impressionante. Ma puoi dividerlo in più istruzioni separate invece? Inoltre, che ne dici di Stored procedure invece?

2
@Adel Quella selezione è in realtà una sottoselezione all'interno di una Stored Procedure. L'intera cosa è piuttosto grande in realtà, ma sono sicuro al 100% che questa è la parte esatta che richiede tempo per essere eseguita.

La modifica del piano di esecuzione, compresi gli indici selezionati automaticamente, molto probabilmente ha a che fare con una modifica dei dati. Vorrei assicurarti che i tuoi indici siano completamente coperti o che il motore prenderà percorsi inaspettati come una scansione della tabella. Suggerisco di rivedere il piano di esecuzione sul nuovo server (senza suggerimenti) per vedere dove si verificano deviazioni dal sistema originale.
Robert Miller,

Vedo. Ho cambiato solo il server, il database è lo stesso, con gli stessi indici. Tuttavia, non sembra scegliere automaticamente di utilizzare i miei indici. Fa esattamente quello che hai detto: una scansione della tabella.
Puffo

Sembra che l'opitmizer di query non gradisca nessuno degli indici della tabella per la tua query. Il piano di esecuzione ha mostrato un indice mancante?
Robert Miller,

Risposte:


7

Penso che in una query su un milione di record, devi evitare cose del genere OUTER JOINS. Ti suggerisco di usare UNION ALLInvece di LEFT JOIN. Finché ritengo CROSS APPLYsia più efficiente della sottoquery nella clausola select, modificherò la query scritta da Conard Frix, che ritengo corretta.

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 stai filtrando le righe con valore null? basta sostituire LEFT JOINcon INNER JOIN, garantisco che diventerà più veloce.

su INDICE:

si prega di notare che quando si dispone di un indice su una tabella, ad esempio

table1(a int, b nvarchar)

e il tuo indice è:

nonclustered index ix1 on table1(a)

e vuoi fare qualcosa del genere:

select a,b from table1
where a < 10

nel tuo indice non hai incluso la colonna bquindi cosa succede?

se sql-server usa il tuo indice, dovrà cercare nell'indice, chiamato "Ricerca indice", quindi fare riferimento alla tabella principale per ottenere la colonna b, chiamata "Cerca" . Questa procedura potrebbe richiedere molto più tempo rispetto alla scansione della tabella stessa: "Scansione tabella" .

ma in base alle statistiche fornite da sql-server, in tali situazioni, potrebbe non utilizzare affatto il tuo indice.

quindi prima di tutto controlla Execution Planper vedere se l'indice è usato affatto.

se sì o no entrambi, modifica il tuo indice per includere tutte le colonne che stai selezionando. dire come:

nonclustered index ix1 on table1(a) include(b)

in questo caso non sarà necessario cercare e la query verrà eseguita molto più velocemente.


1
Non posso cambiare quel join sinistro in Inner join, rovinerebbe i risultati, è una regola aziendale: la seconda tabella non deve necessariamente avere un record correlato. Inoltre, la colonna nella clausola WHERE non accetta valori null.
Puffo

6

È il sottoseleziona nella selezione della colonna che sta causando il ritorno lento. Dovresti provare a utilizzare i sottoselezioni nei join di sinistra o utilizzare una tabella derivata come ho definito di seguito.

Utilizzo dei join sinistri in due istanze della terza tabella

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)

Utilizzando una tabella derivata

 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)

2

Prova invece una croce

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)

Puoi anche usare CTE e row_number o una query inline usando MIN


2

Spostare i bit JOIN fuori dalla parte principale della clausola e metterlo come sottoselezione. Spostarlo nella sezione WHERE e JOIN garantisce che non devi SELEZIONARE ripetutamente TOP 1, che credo sia la ragione della sua lentezza. Se si desidera verificare ciò, esaminare il piano di esecuzione.


2

I ThirdTableriferimenti ((seleziona i sub nel tuo esempio), richiedono la stessa attenzione all'indice di qualsiasi altra parte di una query.

Indipendentemente dal fatto che si utilizzi i sottoseleziona:

(
    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,

JOIN SINISTRA (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 assicurarsi che covering indexessiano definiti per ThirdTable.SomeColumne ThirdTable.SomeOtherColumnche gli indici siano univoci. Ciò significa che sarà necessario qualificare ulteriormente i ThirdTableriferimenti per eliminare la selezione di più righe e migliorare le prestazioni. La scelta di sub selects, LEFT JOINo CROSS APPLYnon avrà importanza fino a quando non migliorerai la selettività per ThirdTable.SomeColumne ThirdTable.SomeOtherColumnincludendo più colonne per garantire una selettività unica. Fino ad allora, mi aspetto che la tua performance continuerà a soffrire.

L' covering indexargomento è ben presentato da Maziar Taheri; pur non ripetendo il suo lavoro, sottolineo la necessità di prendere a cuore l'uso degli indici di copertura.

In breve: migliorare la selettività per le query ThirdTable.SomeColumne ThirdTable.SomeOtherColumn(o i join) aggiungendo colonne nella tabella correlate per garantire una corrispondenza di riga univoca. Se ciò non è possibile, continuerai a soffrire di problemi di prestazioni poiché il motore è impegnato a tirare in file che vengono successivamente gettate via. Ciò influisce sul tuo I / O, CPU e, in definitiva, sul piano di esecuzione.

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.