SQL Server può valutare A <> B
come A < B OR A > B
, anche se una delle espressioni non è deterministica?
Questo è un punto alquanto controverso e la risposta è un "sì" qualificato.
La migliore discussione di cui sono a conoscenza è stata data in risposta alla segnalazione di bug di Itzik Ben-Gan su Bug con NEWID ed espressioni di tabella , che è stata chiusa perché non risolvibile. Da allora Connect è stato ritirato, quindi il collegamento è a un archivio web. Purtroppo, un sacco di materiale utile è stato perso (o reso più difficile da trovare) dalla fine di Connect. Ad ogni modo, le citazioni più utili di Jim Hogg di Microsoft ci sono:
Ciò colpisce il cuore del problema: l'ottimizzazione è autorizzata a modificare la semantica di un programma? Vale a dire: se un programma fornisce determinate risposte, ma viene eseguito lentamente, è legittimo per uno Strumento per ottimizzare le query rendere quel programma più veloce, ma anche cambiare i risultati forniti?
Prima di gridare "NO!" (Anche la mia inclinazione personale :-), considera: la buona notizia è che, nel 99% dei casi, le risposte SONO le stesse. Quindi l'ottimizzazione delle query è una chiara vittoria. La cattiva notizia è che, se la query contiene codice con effetti collaterali, piani diversi possono effettivamente produrre risultati diversi. E NEWID () è una di queste "funzioni" con effetti collaterali (non deterministici) che espone la differenza. [In realtà, se si sperimenta, è possibile escogitare altri - ad esempio, la valutazione di cortocircuito delle clausole AND: fare in modo che la seconda clausola divida per zero una divisione aritmetica - diverse ottimizzazioni possono eseguire quella seconda clausola PRIMA della prima clausola] Ciò riflette Spiegazione di Craig, altrove in questo thread, che SqlServer non garantisce quando vengono eseguiti operatori scalari.
Quindi, abbiamo una scelta: se vogliamo garantire un determinato comportamento in presenza di codice non deterministico (ad effetto collaterale) - in modo che i risultati dei JOIN, ad esempio, seguano la semantica di un'esecuzione a ciclo nidificato - allora noi può utilizzare OPZIONI appropriate per forzare quel comportamento - come sottolinea UC. Ma il codice risultante funzionerà lentamente - questo è il costo, in effetti, di ottimizzare lo Strumento per ottimizzare le query.
Detto questo, stiamo spostando lo Strumento per ottimizzare le query nella direzione del comportamento "come previsto" per NEWID (), scambiando le prestazioni per "risultati come previsto".
Un esempio del cambiamento di comportamento in questo senso nel tempo è che NULLIF funziona in modo errato con funzioni non deterministiche come RAND () . Esistono anche altri casi simili che utilizzano, ad esempio, COALESCE
con una sottoquery che può produrre risultati imprevisti e che vengono anche affrontati gradualmente.
Jim continua:
Chiusura del ciclo. . . Ho discusso questa domanda con il team Dev. E alla fine abbiamo deciso di non modificare il comportamento attuale, per i seguenti motivi:
1) L'ottimizzatore non garantisce tempi o numero di esecuzioni di funzioni scalari. Questo è un principio di lunga data. È il "margine di manovra" fondamentale che consente all'ottimizzatore abbastanza libertà per ottenere miglioramenti significativi nell'esecuzione del piano di query.
2) Questo "comportamento una volta per fila" non è un nuovo problema, sebbene non sia ampiamente discusso. Abbiamo iniziato a modificare il suo comportamento nella versione Yukon. Ma è abbastanza difficile stabilire con precisione, in tutti i casi, esattamente cosa significa! Ad esempio, si applica alle righe intermedie calcolate "lungo la strada" per il risultato finale? - nel qual caso dipende chiaramente dal piano scelto. O si applica solo alle righe che appariranno alla fine nel risultato completato? - c'è una brutta ricorsione in corso qui, poiché sono sicuro che sarai d'accordo!
3) Come accennato in precedenza, per impostazione predefinita "ottimizziamo le prestazioni", il che è positivo per il 99% dei casi. L'1% dei casi in cui potrebbe cambiare i risultati è abbastanza facile da individuare - "funzioni" con effetti collaterali come NEWID - e facili da "correggere" (il trading di perf, di conseguenza). L'impostazione predefinita per "ottimizzare le prestazioni" è di nuovo consolidata e accettata. (Sì, non è la posizione scelta dai compilatori per i linguaggi di programmazione convenzionali, ma così sia).
Quindi, i nostri consigli sono:
a) Evitare di fare affidamento su tempi non garantiti e semantica del numero di esecuzioni. b) Evitare di utilizzare NEWID () in profondità nelle espressioni di tabella. c) Usa OPTION per forzare un comportamento particolare (trading perf)
Spero che questa spiegazione ci aiuti a chiarire le nostre ragioni per chiudere questo bug come "non risolverà".
È interessante notare che AND NOT (s_guid = NEWID())
produce lo stesso piano di esecuzione
Questa è una conseguenza della normalizzazione, che si verifica molto presto durante la compilazione delle query. Entrambe le espressioni vengono compilate esattamente nella stessa forma normalizzata, quindi viene prodotto lo stesso piano di esecuzione.