2012-05-22 20 views
9

Disculpas si esto no es adecuado, pero en realidad se trata de un 'por qué' en lugar de un 'cómo'. No estoy seguro de que sea adecuado, pero no conozco un lugar mejor para preguntar y no puedo pensar cómo expresar un google para obtener lo que estoy buscando.Evaluación SQL de las cláusulas IF

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

Mire esa afirmación. No hay mundo en el que la cláusula IF sea verdadera. Si intento ejecutarlo, estoy esperando que SQL salte la cláusula IF y se mueva hasta el final. En su lugar obtengo:

An invalid floating point operation occurred. 

Esto es extraño. Así que supongo que esa es la forma en que SQL lo hace. Excepto ...

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

No hay ningún error aquí. La declaración en la cláusula IF aún debe generar un error. ¿Alguien podría explicar por qué esto no está sucediendo?

Esto surgió mientras se depuraba un conjunto masivo de calcos SQL donde EXP (SUM (LOG())) se usa para acumular datos dentro de una cláusula if. Puedo alterar el código para evitar que vuelva a suceder, pero ¿por qué está evaluando algo dentro de una cláusula IF que no se cumple?

Saludos.

EDITAR: Diversión adicional. ¿Trata de atraparlo? Pffft

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

no matemática:

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

Respuesta

7

Mi conjetura sería que log(0) se evalúa de manera efectiva antes de tiempo debido a constant-folding mientras que 1/0 no es, ya sea debido a su estimación de cardinalidad o más probablemente el hecho de que el ajuste ANSI_WARNINGS afectará el resultado deseado de una división por cero (desbordamiento vs NULO).

+0

Las advertencias de ANSI no hicieron ninguna diferencia, pero ese es el enlace que creo que necesitaba, así que muchas gracias :) –

+0

Su bienvenida. Por ANSI_WARNINGS quise decir que si se emitiera un "set ansi_warnings off" en cualquier momento, entonces no vería un error de división por cero. Como el compilador nunca puede saber de antemano el estado del indicador ANSI_WARNINGS cuando se ejecuta la consulta compilada, debe ignorar cualquier expresión plegada que genere una excepción de divide por cero en el momento de la compilación, ya que no puede predecir el comportamiento deseado. (Si se garantiza que ansi_warnings siempre estará desactivado, 1/0 se doblaría a NULL) –

2

El analizador simplemente no tiene la inteligencia para seguir su lógica SI. Consideremos el siguiente ejemplo:

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

Si lo ejecutas o incluso sólo analizarlo, el analizador se ve en todos los CREATE TABLE instrucciones del lote y determina que ha intentado crear la tabla dos veces (la primera copia, obviamente, doesn debe existir para que esto suceda). Resultado:

Msg 2714, nivel 16, estado 1, línea 7
Ya hay un objeto denominado '#t' en la base de datos.

Realmente no sé si tengo una mejor respuesta para usted que el analizador no es tan inteligente como usted.

Puede vencer al analizador posponiendo el problema hasta el tiempo de ejecución mediante SQL dinámico, p. Ej.

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

Pero entonces tendría que preguntarse, ¿cuál es el punto de establecer el andamiaje para una condición que nunca va a ser verdad, y la emisión de una declaración de que usted sabe que va a error?

+0

Hay alrededor de 5 condiciones IF. En uno de ellos se realiza un cálculo de los datos que en las demás condiciones provocarán errores. Estos ejemplos son triviales para mostrar el comportamiento. Era más como IF [Datos no causarán error] "Hacer calc" END –

+0

También, curiosamente analiza bien, pero no se ejecuta con éxito –

+0

Sí, el analizador no siempre detecta todos los tipos de errores, y detecta un poco de antemano que nunca serían errores en el tiempo de ejecución. Como dije, no es el más inteligente. Pero tiene un * lote * de reglas y algoritmos complejos para determinar lo que permitirá analizar correctamente y lo que no.Estas reglas no están documentadas, por lo que @GordonLinoff explicó que solo tienes que saber qué tipo de cosas harán tropezar al analizador y qué tipo de cosas dejarán pasar. –

0

Claramente, hay algunas expresiones que el compilador de SQL está tratando de evaluar en tiempo de compilación versus en tiempo de ejecución. Mi suposición es que una división no se considera demasiado cara, por lo que se pospone al tiempo de ejecución. Por otro lado, algo realmente complicado como un log() es costoso y quieren hacerlo en tiempo de compilación.

Esto es una suposición. También significa que tales diferencias no son deterministas.Debe averiguar cuál funciona o no funciona en una secuencia de comandos en particular, y el comportamiento puede cambiar entre lanzamientos de la base de datos.

2

Si intento plazo que estoy esperando SQL para saltar más allá de la cláusula IF y pasar a la final.

Cuando se ejecuta el lote tres cosas suceden

  1. Su SQL se analiza

  2. Su SQL se compila

  3. se ejecuta SQL

Lo que es desconocido rtunate es que los errores tanto de compilación como de ejecución en un lote en el servidor SQL dan como resultado el mismo mensaje "Consulta completada con errores". Por lo que permite utilizar un procedimiento en donde es más fácil ver la diferencia

Considere el siguiente

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

Este procedimiento analiza bien. Sin embargo, no se puede compilar a menos que eliminemos los primeros tres SELECT porque tenemos algunas constantes que no son válidas como parámetros. Sería bueno si SELECT 1/0 también causó un error de tiempo de compilación en lugar de un error de tiempo de ejecución, pero como @Alex K señala que el comportamiento se basa en ANSI_WARNINGS por lo que no es un error de tiempo de compilación.

Es por eso que vemos diferencias entre los dos primeros. También explica por qué TRY CATCH no funcionó ya que es un error de tiempo de compilación.

Ahora, ¿por qué el servidor SQL compila el código inalcanzable? Porque en general para saber que es inalcanzable requiere una solución al problema de detención. Puede solucionarlo para algunos casos, pero luego esto ...


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

tendrían un comportamiento diferente que es aún más confuso.

if (1=0) 
    SomeCompiletime error 
Cuestiones relacionadas