2012-05-22 19 views
9

Ci scusiamo se questo non è adatto, ma in realtà questo è un "perché" piuttosto che un "come". Non sono sicuro che sia adatto, ma non conosco un posto migliore dove chiedere e non riesco a pensare a come dire a un google per ottenere quello che sto cercando.Valutazione SQL delle clausole IF

IF 'hell' = 'freezing over' 
    BEGIN 
    SELECT log(0) 
    END 

Guarda questa affermazione. Non esiste un mondo in cui la clausola IF sia vera. Se provo a eseguirlo, mi aspetto che SQL passi oltre la clausola IF e passi alla fine. Invece ottengo:

An invalid floating point operation occurred. 

Questo è bizzarro. Quindi immagino che sia proprio il modo in cui SQL fa la cosa. Tranne ...

IF 'hell' = 'freezing over' 
    BEGIN 
    SELECT 1/0 
    END 

Non c'è nessun errore qui. L'affermazione nella clausola IF dovrebbe comunque generare un errore. Qualcuno potrebbe spiegare perché questo non sta accadendo?

Questo si è verificato durante il debugging di un enorme set di calcoli SQL in cui EXP (SUM (LOG())) viene utilizzato per accumulare dati all'interno di una clausola if. Posso modificare il codice per impedire che ciò accada di nuovo, ma perché sta valutando qualcosa all'interno di una clausola IF che non è soddisfatta.

Cheers.

MODIFICA: divertimento aggiuntivo. Prova a prendere? Pffft

IF 1=2 
    BEGIN 
     BEGIN TRY 
      SELECT SQRT(-1) 
     END TRY 
     BEGIN CATCH 
     END CATCH 
    END 

non matematico:

IF 1=2 
    BEGIN 
    SELECT SUBSTRING('hello',-1,-1) 
    END 

risposta

7

La mia ipotesi è che log(0) è effettivamente valutato prematuramente a causa di constant-folding mentre 1/0 non è, né per la sua stima di cardinalità o più probabilmente il fatto che l'impostazione ANSI_WARNINGS influenzerà il risultato desiderato di una divisione per zero (overflow vs NULLO).

+0

Gli avvertimenti ANSI non hanno fatto alcuna differenza, ma questo è il collegamento che penso di averne bisogno, grazie mille :) –

+0

Benvenuto. Con ANSI_WARNINGS intendevo che se un "set ansi_warnings off" fosse emesso in qualsiasi momento, non vedresti un errore di divisione per zero. Poiché il compilatore non può mai sapere in anticipo lo stato del flag ANSI_WARNINGS quando viene eseguita la query compilata, deve ignorare qualsiasi espressione piegata che eleva una divisione per eccezione zero al momento della compilazione in quanto non è in grado di prevedere il comportamento desiderato. (Se fosse garantito che ansi_warnings fosse sempre disattivato, 1/0 passerebbe a NULL) –

2

Il parser semplicemente non ha l'intelligenza di seguire la logica IF. Si consideri il seguente esempio:

IF (1 = 0) 
BEGIN 
    CREATE TABLE #t(x INT): 
END 
ELSE 
BEGIN 
    CREATE TABLE #t(x INT): 
END 

Se lo si esegue o anche solo analizza esso, il parser esamina tutti i CREATE TABLE istruzioni del batch e determina che si è tentato di creare la tabella due volte (la prima copia, ovviamente doesn devo esistere perché ciò accada). Risultato:

Msg 2714, livello 16, stato 1, riga 7
C'è già un oggetto denominato '#t' nel database.

Non so davvero se ho una risposta migliore per te che il parser non è intelligente come te.

È possibile disattivare il parser rimandando il problema fino al runtime utilizzando SQL dinamico, ad es.

IF 'hell' = 'freezing over' 
BEGIN 
    EXEC sp_executesql N'SELECT log(0);'; 
END 

Ma poi mi chiedo, qual è il punto di creazione del ponteggio per una condizione che non potrà mai essere vera, e l'emissione di una dichiarazione che si sa sta per errore?

+0

Ci sono circa 5 condizioni IF. In uno di essi viene eseguito un calcolo su dati che nelle altre condizioni causeranno errori. Questi esempi sono banali per mostrare il comportamento. Era più simile a SE [I dati non causano errori] "Calcola" END –

+0

Inoltre, è interessante analizzare correttamente, ma non viene eseguito correttamente –

+0

Sì, il parser non rileva sempre tutti i tipi di errori in anticipo, e ne prende in anticipo alcuni che non sarebbero mai errori in fase di esecuzione. Come ho detto, non è il più intelligente. Ma ha * un sacco * di regole e algoritmi complessi per determinare cosa permetterà di analizzare correttamente e cosa no.Queste regole non sono documentate in modo che @GordonLinoff spieghi che devi solo sapere che tipo di cose faranno scattare il parser e che tipo di cose lascerà scorrere. –

0

Chiaramente, ci sono alcune espressioni che il compilatore SQL sta cercando di valutare in fase di compilazione rispetto al momento dell'esecuzione. La mia ipotesi è che una divisione sia considerata non troppo costosa, quindi la rimanda al tempo di esecuzione. D'altra parte, qualcosa di veramente complicato come un log() è costoso e vogliono farlo in fase di compilazione.

Questa è una supposizione. Significa anche che tali differenze non sono deterministiche.Devi capire quale lavoro funziona o non lavorare in un particolare script - e il comportamento potrebbe cambiare tra le versioni del database.

2

Se tento di corsa che mi aspetto SQL per saltare oltre la clausola di IF e spostarsi alla fine.

Quando si esegue il batch tre cose che succedono

  1. SQL viene analizzato

  2. SQL viene compilato

  3. viene eseguito SQL

Cosa c'è unfo rtunate è che sia gli errori di compilazione che quelli di esecuzione in un batch nel server SQL generano lo stesso messaggio "Query completata con errori". Quindi, consente di utilizzare una procedura in cui è più facile vedere la differenza

Si consideri il seguente

Create proc compiletime 
as 
SELECT log(0) 
SELECT SQRT(-1) 
SELECT SUBSTRING('hello',-1,-1) 
SELECT 1/0 

Tale procedura analizza bene. Tuttavia non può essere compilato a meno che non rimuoviamo i primi SELECTs perché abbiamo alcune costanti che non sono valide come parametri. Sarebbe bello se SELECT 1/0 causasse anche un errore in fase di compilazione invece di un errore di run time ma come @Alex K sottolinea che il comportamento è basato su ANSI_WARNINGS quindi non è un errore di compilazione.

Ecco perché vediamo le differenze tra i primi due. Spiega anche perché TRY CATCH non ha funzionato dal momento che è un errore in fase di compilazione.

Ora perché SQL Server compila codice non raggiungibile. Perché in generale per sapere che è irraggiungibile richiede una soluzione al problema di interruzione. È possibile risolvere per alcuni casi, ma allora questo ...


DECLARE @x as integer 
SET @x = SomeFunction() 
If (1 = @x) 
    SomeCompiletime error 

avrebbe un comportamento diverso, che è ancora più confusa.

if (1=0) 
    SomeCompiletime error 
Problemi correlati