Perché l'errore di query con set di risultati vuoto in SQL Server 2012?


31

Quando si eseguono le seguenti query in MS SQL Server 2012, la seconda query ha esito negativo ma non la prima. Inoltre, se eseguito senza le clausole where, entrambe le query falliranno. Sono in perdita perché uno dei due fallirebbe poiché entrambi dovrebbero avere set di risultati vuoti. Qualsiasi aiuto / approfondimento è apprezzato.

create table #temp
(id     int primary key)

create table #temp2
(id     int)

select 1/0
from #temp
where id = 1

select 1/0
from #temp2
where id = 1

Risposte:


39

Un primo sguardo ai piani di esecuzione mostra che l'espressione 1/0è definita negli operatori di calcolo scalare:

Piani grafici

Ora, anche se i piani di esecuzione iniziano a essere eseguiti all'estrema sinistra, chiamando ripetutamente Opene GetRowmetodi su iteratori secondari per restituire risultati, SQL Server 2005 e versioni successive contengono un'ottimizzazione per cui le espressioni sono spesso definite solo da uno scalare di calcolo, con valutazione differita fino a una successiva l'operazione richiede il risultato :

Gli operatori scalari di calcolo che compaiono negli Showplan generati da SET STATISTICS XML potrebbero non contenere l'elemento RunTimeInformation.  In Showplan grafici, Righe effettive, Riavvolgimenti effettivi e Riavvolgi effettivi potrebbero essere assenti dalla finestra Proprietà quando l'opzione Includi piano di esecuzione effettivo è selezionata in SQL Server Management Studio.  In questo caso, significa che sebbene questi operatori siano stati utilizzati nel piano di query compilato, il loro lavoro è stato eseguito da altri operatori nel piano di query runtime.  Si noti inoltre che il numero di esecuzioni nell'output Showplan generato da SET STATISTICS PROFILE è equivalente alla somma di rebind e riavvolgimenti in Showplan generati da SET STATISTICS XML.  Da: MSDN Books Online

In questo caso, il risultato dell'espressione è necessario solo quando si assembla la riga per il ritorno al client (cosa che si può pensare di accadere SELECTsull'icona verde ). Secondo tale logica, una valutazione differita significherebbe che l'espressione non viene mai valutata perché nessuno dei due piani genera una riga di ritorno. Per elaborare un po 'il punto, né la Ricerca indice cluster né la Scansione tabella restituiscono una riga, quindi non vi è alcuna riga da assemblare per il ritorno al client.

Tuttavia, esiste un'ottimizzazione separata per cui alcune espressioni possono essere identificate come costanti di runtime e quindi valutate una volta prima dell'inizio dell'esecuzione della query . In questo caso, un'indicazione che si è verificata può essere trovata nello showplan XML (piano di ricerca dell'indice cluster a sinistra, piano di scansione della tabella a destra):

Showplan XML

Ho scritto di più sui meccanismi sottostanti e su come possono influenzare le prestazioni in questo post del blog . Utilizzando le informazioni fornite lì, possiamo modificare la prima query in modo che entrambe le espressioni vengano valutate e memorizzate nella cache prima dell'inizio dell'esecuzione:

select 1/0 * CONVERT(integer, @@DBTS)
from #temp
where id = 1

select 1/0
from #temp2
where id = 1

Ora, il primo piano contiene anche un riferimento di espressione costante ed entrambe le query producono il messaggio di errore. L'XML per la prima query contiene:

Espressione costante

Ulteriori informazioni: calcolare scalari, espressioni e prestazioni


21

Ho intenzione di indovinare in modo intelligente (e nel processo probabilmente attirare un guru di SQL Server che potrebbe dare una risposta davvero dettagliata).

La prima query si avvicina all'esecuzione come:

  1. Scansiona l'indice della chiave primaria
  2. Cerca i valori nella tabella dei dati necessari per la query

Sceglie questo percorso perché hai una whereclausola sulla chiave primaria. Non arriva mai al secondo passaggio, quindi la query non fallisce.

Il secondo non ha una chiave primaria su cui eseguire, quindi si avvicina alla query come:

  1. Esegui una scansione completa della tabella dei dati e recupera i valori necessari

Uno di questi valori sta 1/0causando il problema.

Questo è un esempio di SQL Server che ottimizza la query. Per la maggior parte, questa è una buona cosa. SQL Server sposta le condizioni dall'operazione di selectscansione nella tabella. Questo spesso consente di risparmiare passaggi nella valutazione della query.

Ma questa ottimizzazione non è una cosa positiva. In effetti, sembra violare la stessa documentazione di SQL Server che afferma che la whereclausola viene valutata prima di select. Bene, potrebbero avere qualche spiegazione erudita sul significato di questo. Per la maggior parte degli umani, tuttavia, elaborare logicamente il whereprecedente selectsignificherebbe (tra le altre cose) "non generare selecterrori -clause su righe non restituite all'utente".


1
+1 nessun indizio se hai ragione, ma la risposta migliore che posso vedere data l'unica differenza è la chiave primaria.

1
@GordonLinoff Paul Randal ha appena confermato via Twitter che la tua risposta è stata eccezionale.
SchmitzIT,

4
@Still, l'attuale ordine di esecuzione, per quanto diverso, non dovrebbe portare a messaggi di errore del genere.
ypercubeᵀᴹ

7
@ypercube Erland Sommarskog sarebbe d'accordo con te (elemento Connect)
Paul White dice GoFundMonica

2
Grazie per il puntatore: ho effettuato l'accesso e ho votato a fondo la richiesta.
Gordon Linoff,
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.