¿Se permite que SQL Server evalúe A <> B
como A < B OR A > B
, incluso si una de las expresiones no es determinista?
Este es un punto algo controvertido, y la respuesta es un "sí" calificado.
La mejor discusión que conozco se dio en respuesta al informe de error Connect de Itzik Ben-Gan Error con NEWID y Expresiones de tabla , que se cerró porque no se solucionará. Connect se ha retirado desde entonces, por lo que el enlace está a un archivo web. Lamentablemente, la desaparición de Connect perdió mucho material útil (o lo hizo más difícil de encontrar). De todos modos, las citas más útiles de Jim Hogg de Microsoft son:
Esto llega al meollo del problema: ¿se permite la optimización para cambiar la semántica de un programa? Es decir: si un programa produce ciertas respuestas, pero se ejecuta lentamente, ¿es legítimo que un Optimizador de consultas haga que ese programa se ejecute más rápido, pero también cambie los resultados dados?
Antes de gritar "¡NO!" (mi propia inclinación personal también :-), considere: la buena noticia es que, en el 99% de los casos, las respuestas SON las mismas. Entonces, la optimización de consultas es una clara victoria. La mala noticia es que, si la consulta contiene un código de efectos secundarios, los diferentes planes PUEDEN producir resultados diferentes. Y NEWID () es una de esas 'funciones' de efectos secundarios (no deterministas) que expone la diferencia. [En realidad, si experimenta, puede idear otros, por ejemplo, evaluación de cortocircuito de cláusulas AND: hacer que la segunda cláusula arroje una división aritmética por cero; diferentes optimizaciones pueden ejecutar esa segunda cláusula ANTES de la primera cláusula] Esto refleja La explicación de Craig, en otra parte de este hilo, que SqlServer no garantiza cuando se ejecutan operadores escalares.
Entonces, tenemos una opción: si queremos garantizar un cierto comportamiento en presencia de un código no determinista (efecto secundario), de modo que los resultados de JOIN, por ejemplo, sigan la semántica de una ejecución de bucle anidado, entonces puede usar las OPCIONES apropiadas para forzar ese comportamiento, como lo señala UC. Pero el código resultante se ejecutará lentamente: ese es el costo de, en efecto, obstaculizar el Optimizador de consultas.
Dicho todo esto, estamos moviendo el Optimizador de consultas en la dirección del comportamiento "según lo esperado" para NEWID (), intercambiando el rendimiento por "resultados según lo esperado".
Un ejemplo del cambio de comportamiento a este respecto a lo largo del tiempo es que NULLIF funciona incorrectamente con funciones no deterministas como RAND () . También hay otros casos similares que se usan, por ejemplo, COALESCE
con una subconsulta que puede producir resultados inesperados y que también se están abordando gradualmente.
Jim continúa:
Cerrando el ciclo . . . He discutido esta pregunta con el equipo de desarrollo. Y finalmente hemos decidido no cambiar el comportamiento actual, por las siguientes razones:
1) El optimizador no garantiza el tiempo ni el número de ejecuciones de funciones escalares. Este es un principio largamente establecido. Es el 'margen de maniobra' fundamental que le da al optimizador suficiente libertad para obtener mejoras significativas en la ejecución del plan de consulta.
2) Este "comportamiento de una vez por fila" no es un problema nuevo, aunque no se discute ampliamente. Comenzamos a modificar su comportamiento en el lanzamiento de Yukon. ¡Pero es bastante difícil precisar con precisión, en todos los casos, exactamente lo que significa! Por ejemplo, ¿se aplica a las filas intermedias calculadas 'en el camino' al resultado final? - en cuyo caso, depende claramente del plan elegido. ¿O se aplica solo a las filas que finalmente aparecerán en el resultado completado? - Hay una desagradable recursión aquí, ¡estoy seguro de que estarás de acuerdo!
3) Como mencioné anteriormente, por defecto "optimizamos el rendimiento", lo cual es bueno para el 99% de los casos. El 1% de los casos en los que podría cambiar los resultados es bastante fácil de detectar ('funciones' de efectos secundarios como NEWID) y fácil de 'corregir' (rendimiento comercial, como consecuencia). Este valor predeterminado para "optimizar el rendimiento" nuevamente, está establecido desde hace mucho tiempo y es aceptado. (Sí, no es la postura elegida por los compiladores para los lenguajes de programación convencionales, pero que así sea).
Entonces, nuestras recomendaciones son:
a) Evite la confianza en el tiempo no garantizado y la semántica del número de ejecuciones. b) Evite usar NEWID () en las expresiones de tabla. c) Use OPTION para forzar un comportamiento particular (rendimiento comercial)
Espero que esta explicación ayude a aclarar nuestras razones para cerrar este error, ya que "no se solucionará".
Curiosamente, AND NOT (s_guid = NEWID())
produce el mismo plan de ejecución
Esto es una consecuencia de la normalización, que ocurre muy temprano durante la compilación de consultas. Ambas expresiones se compilan exactamente en la misma forma normalizada, por lo que se produce el mismo plan de ejecución.