Perché SQL Server restituisce alcune righe durante l'esecuzione della query e talvolta no?


33

Ci sono query in cui quando si preme "esegui", mostra alcune righe e continua a crescere, ma la query non è ancora finita. Eppure a volte attende fino alla fine della query.

Perché succede? C'è un modo per controllarlo?

Risposte:


43

La risposta, come al solito (va bene, la maggior parte delle volte), sta nel piano di esecuzione.

Esistono alcuni operatori che richiedono che tutte le righe arrivino prima di poter iniziare a elaborarle e passarle a valle, ad esempio:

  • Hash Join (sulla creazione della tabella hash)
  • Hash Match
  • Ordina (eccetto Hash Flow Distinct)

Vengono chiamati operatori di blocco o stop and go per questo motivo e vengono spesso scelti quando l'ottimizzatore pensa che dovrà elaborare un sacco di dati per trovare i tuoi dati.

Esistono altri operatori in grado di avviare lo streaming o passare immediatamente qualsiasi riga trovata

  • Cicli annidati
  • Indice supportato Unisci join
  • Stream Aggregates

Quando le query iniziano a restituire immediatamente i dati, ma non finiscono immediatamente, di solito è un segno che l'ottimizzatore ha scelto un piano per individuare e restituire rapidamente alcune righe utilizzando operatori con un costo di avvio inferiore.

Ciò può accadere a causa degli obiettivi di riga introdotti da te o dall'ottimizzatore.

Può anche accadere se per qualche motivo viene scelto un piano errato (mancanza di SARGability, sniffing dei parametri, statistiche insufficienti, ecc.), Ma ciò richiede più scavi per capire.

Per ulteriori informazioni, consulta il blog di Rob Farley qui

E la serie di Paul White sui goal di fila qui , qui , qui e qui .

Va anche notato che, se si parla di SSMS, le righe vengono visualizzate solo dopo aver riempito un intero buffer, non solo volenti o nolenti.


14

Se capisco cosa stai osservando, questo è il modo in cui Management Studio esegue il rendering delle righe e ha poco a che fare con il modo in cui SQL Server restituisce le righe. Infatti, spesso quando si restituiscono grandi risultati a SSMS e si tenta di renderli in una griglia, SSMS non riesce a tenere il passo e SQL Server finisce per attendere che l'app elabori più righe. In questo caso vedrai che SQL Server accumula le ASYNC_NETWORK_IOattese.

Puoi controllarlo in qualche modo usando Risultati su testo anziché Risultati su griglia, poiché SSMS può disegnare il testo più velocemente di quanto possa disegnare griglie, ma probabilmente scoprirai che ciò può influire sulla leggibilità a seconda del numero di colonne e dei tipi di dati coinvolti. Entrambi sono influenzati da quando SSMS decide di scrivere effettivamente i risultati in quel riquadro, che dipende da quanto è pieno il buffer di output.

Quando si hanno più istruzioni e si desidera forzare il buffer a eseguire il rendering dei risultati di output nel riquadro dei messaggi, è possibile utilizzare un piccolo trucco di stampa tra le istruzioni:

RAISERROR('', 0, 1) WITH NOWAIT;

Ma questo non aiuta quando stai cercando di far sì che SSMS esegua il rendering delle righe più rapidamente quando tutto l'output proviene da una singola istruzione.

Più direttamente, puoi controllarlo limitando il numero di risultati che stai visualizzando in SSMS. Vedo spesso le persone lamentarsi del tempo impiegato per restituire un milione di righe alla griglia. Cosa diavolo farà qualcuno con un milione di righe in una griglia SSMS, non ne ho idea.

Ci sono alcuni hack come OPTION (FAST 100), che ottimizzeranno per recuperare quelle prime 100 righe (o qualsiasi 100 righe se non c'è esterno ORDER BY), ma questo può comportare un costo molto più lento di recupero per il resto delle righe e un piano che è più nel complesso inefficiente, quindi non è davvero un'opzione di riferimento IMHO.


1

La tua domanda non riguarda SQL Server in sé ma:

  • Server SQL
  • Rete
  • SSMS come applicazione client

C'è un modo per controllarlo?

Risposta breve :

  1. Prova sqlcmdinvece di ssmso sqlcmd-mode dissms
  2. Controlla le impostazioni di connessione e sessione

Risposta lunga :

Ovviamente! Ma non uno - prob

  1. Eseguire la query con sqlcmdo in sqlcmd-mode in ssms.
  2. Se si desidera escludere il ruolo della rete, eseguire la query sul server con connessione alla memoria condivisa.
  3. Se le prestazioni della query non sono soddisfacenti anche con la connessione alla memoria condivisa, analizzare i piani di esecuzione. Se la query non funziona correttamente attraverso la rete, chiamare l'amministratore di rete per assistenza. Se la tua query funziona male solo in SSMS, leggi di più.
  4. Ora siamo sicuri che i problemi sono sul lato client (ssms in questo caso). Guarda le impostazioni di connessione e sessione in SSMS. Non credere all'interfaccia di ssms e controlla con SQL Profiler: trova la tua connessione spide ottieni l'elenco completo delle impostazioni della sessione. Confronta con le impostazioni della sqlcmdsessione. Se nulla fa clic: copia tutte le impostazioni della sessione dal profiler nello script di query, esegui in sqlcmdmodalità e cambiando gradualmente le impostazioni troverai il tuo colpevole.

In bocca al lupo!


-2

Per aggiungere alla risposta sp_BlitzErik, prendi l'esempio usando a NOT IN ()con una sottoselezione . Per determinare se un elemento è nel risultato della query nidificata, è (generalmente) necessario recuperare l'intero risultato.

Quindi un modo semplice che ho trovato per migliorare le prestazioni di tali query è quello di riscriverle come una LEFT OUTER JOINcondizione in cui la condizione per il RIGHTlato è nulla (puoi capovolgerla, ovviamente, ma chi usa RIGHT OUTER JOINS?). Ciò consente ai risultati di iniziare a tornare immediatamente.


Io non la penso così. Se le colonne confrontate non sono nulla, i risultati dovrebbero essere gli stessi e i piani - di solito - gli stessi, per le 3 versioni di un antijoin (NOT IN, NOT EXISTS, LEFT JOIN / IS NULL). Non è necessario recuperare l'intero risultato.
ypercubeᵀᴹ

Se la sottoselezione è davvero complessa di cui la query prodotta deve valutare l'intera sottoselezione prima di controllare la condizione NOT IN WHERE t.x IN (<complex SELECT subquery>), l'equivalente LEFT JOIN LEFT JOIN (<complex SELECT subquery>) AS r ON r.x = t.x .... WHERE r.x IS NULL, quindi anche la sottoquery dovrà essere valutata (quindi lo stesso piano complesso con NOT Nella versione).
ypercubeᵀᴹ

@ ypercubeᵀᴹ Ha funzionato per me in passato. Ho visto che le query passano da minuti a tornare al secondo.
JimmyJames,

@ ypercubeᵀᴹ Ho messo insieme un semplice esempio in Oracle (mi dispiace, al momento non ho accesso a SQL Server) e sicuramente avevano piani di spiegazione diversi. Forse non erano differenze significative ma sembrano piuttosto diverse.
JimmyJames,

@JimmyJames: non è un modo semplice se vuoi prestazioni stabili e tali "ottimizzazioni" sono molto sensibili per la versione di SQL Server. E non commettere errori interessanti per Oracle (quale versione?). Storicamente SQL Server preferiva, NOT EXISTSma Oracle NOT INnelle query. Ma oggi deve essere considerato un errore nel generatore di piani
Alex Yu,
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.