Evaluación SQL de cláusulas IF

Disculpas si esto no es adecuado, pero en realidad esto es 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 en cómo formular un google para obtener lo que estoy buscando.

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

Mira esa declaración. No hay mundo en el que la cláusula IF sea verdadera. Si intento ejecutarlo, espero que SQL salte la cláusula IF y avance hasta el final. En cambio, 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 debería generar un error. ¿Alguien podría explicar por qué esto no está sucediendo?

Esto surgió durante la depuración de un conjunto masivo de cálculos SQL donde EXP(SUM(LOG())) se usa para acumular datos dentro de una cláusula if. Puedo modificar el código para evitar que eso 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ático:

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

preguntado el 22 de mayo de 12 a las 15:05

4 Respuestas

Mi suposición sería que log(0) efectivamente se evalúa prematuramente debido a plegado constante mientras 1/0 no lo es, ya sea debido a su estimación de cardinalidad o, más probablemente, al hecho de que la configuración de ANSI_WARNINGS afectará el resultado deseado de una división por cero (desbordamiento frente a NULL).

contestado el 22 de mayo de 12 a las 15:05

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

De nada. 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. Dado que 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 división por cero en el momento de la compilación, ya que no puede predecir el comportamiento deseado. (Si se garantizara que ansi_warnings siempre estaría desactivado, 1/0 se retiraría a NULL) - Alex K.

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

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

Ya sea que lo ejecute o simplemente lo analice, el analizador analiza todos los CREATE TABLE declaraciones en el lote y determina que intentó crear la tabla dos veces (obviamente, la primera copia no tiene que existir para que esto suceda). Resultado:

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

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

Puede derrotar al analizador aplazando el problema hasta el tiempo de ejecución utilizando SQL dinámico, por ejemplo

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

Pero entonces tendría que preguntarme, ¿cuál es el punto de establecer el andamiaje para una condición que nunca será cierta y emitir una declaración que sabes que va a ser un error?

contestado el 22 de mayo de 12 a las 15:05

Hay alrededor de 5 condiciones IF. En uno de ellos se hace un cálculo sobre datos que en el resto de condiciones darán error. Estos ejemplos son triviales para mostrar el comportamiento. Era más como IF [Data won't cause error] "Do calc" END - norte

Además, curiosamente, analiza bien, pero no se ejecuta con éxito: norte

Sí, el analizador no siempre detecta todos los tipos de errores por adelantado, y detecta algunos por adelantado que nunca serían errores en tiempo de ejecución. Como dije, no es el más inteligente. Pero tiene un montón de reglas y algoritmos complejos para determinar qué permitirá analizar con éxito y qué no. Estas reglas no están documentadas, por lo que, como explicó @GordonLinoff, solo tiene que saber qué tipo de cosas harán tropezar al analizador y qué tipo de cosas dejarán pasar. - Aarón Bertrand

si intento corrida Estoy esperando que SQL salte más allá de la cláusula IF y avance hasta el final.

Cuando ejecuta su lote, suceden tres cosas

  1. Su SQL es analizado

  2. Su SQL está compilado

  3. Su SQL se ejecuta

Lo lamentable es que tanto los errores de compilación como de ejecución en un lote en el servidor SQL dan como resultado el mismo mensaje de "Consulta completada con errores". Así que usemos un Procedimiento donde sea más fácil ver la diferencia.

Considera lo siguiente

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

Ese procedimiento analiza bien. Sin embargo, no se puede compilar a menos que eliminemos los tres primeros 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 señala @Alex K, el comportamiento se basa en ANSI_WARNINGS, por lo que no es un error de tiempo de compilación.

Por eso vemos diferencias entre cuando 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 código inalcanzable? Porque en general para saber que es inalcanzable se requiere una solución al problema de la detención. Puede resolverlo para algunos casos, pero luego esto ...


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

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

if (1=0)
   SomeCompiletime error

Respondido el 20 de junio de 14 a las 12:06

Claramente, hay algunas expresiones que el compilador de SQL intenta evaluar en tiempo de compilación versus en tiempo de ejecución. Supongo que una división no se considera demasiado costosa, por lo que la pospone al tiempo de ejecución. Por otro lado, algo realmente complicado como 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 en un script en particular, y el comportamiento puede cambiar entre las versiones de la base de datos.

contestado el 22 de mayo de 12 a las 15:05

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.