Estoy usando una función T-SQL COALESCE
donde el primer argumento no será nulo en aproximadamente el 95% de las veces que se ejecuta. Si el primer argumento es NULL
, el segundo argumento es un proceso bastante largo:
SELECT COALESCE(c.FirstName
,(SELECT TOP 1 b.FirstName
FROM TableA a
JOIN TableB b ON .....)
)
Si, por ejemplo, c.FirstName = 'John'
¿SQL Server seguiría ejecutando la subconsulta?
Sé que con la IIF()
función VB.NET , si el segundo argumento es True, el código sigue leyendo el tercer argumento (aunque no se usará).
fuente
CASE
siempre evalúe los circuitos de izquierda a derecha y siempre cortocircuitos )SELECT COALESCE((SELECT CASE WHEN RAND() <= 0.5 THEN 1 END), 1);
- repetir varias veces. Obtendrás aNULL
veces. Inténtalo de nuevo conISNULL
- nunca obtendrásNULL
...¿Qué tal este, según me informó Itzik Ben-Gan, a quien Jaime Lafargue le contó ?
Resultado:
Hay soluciones triviales, por supuesto, pero el punto es que
CASE
no siempre garantiza una evaluación / cortocircuito de izquierda a derecha. Informé el error aquí y se cerró como "por diseño". Posteriormente, Paul White archivó este elemento Connect y se cerró como Fijo. No porque se haya solucionado per se, sino porque actualizaron Books Online con una descripción más precisa del escenario en el que los agregados pueden cambiar el orden de evaluación de unaCASE
expresión. Recientemente escribí más sobre esto aquí .EDITE solo un apéndice, aunque estoy de acuerdo en que estos son casos extremos, que la mayoría de las veces puede confiar en la evaluación de izquierda a derecha y en cortocircuito, y que estos son errores que contradicen la documentación y probablemente eventualmente se solucionarán ( esto no es definitivo: vea la conversación de seguimiento en la publicación del blog de Bart Duncan para ver por qué), tengo que estar en desacuerdo cuando la gente dice que algo siempre es cierto, incluso si hay un caso de borde único que lo refuta. Si Itzik y otros pueden encontrar errores solitarios como este, al menos es posible que también haya otros errores. Y dado que no conocemos el resto de la consulta del OP, no podemos decir con certeza que dependerá de este cortocircuito, pero terminará siendo mordido por él. Entonces, para mí, la respuesta más segura es:
Si bien generalmente puede confiar
CASE
para evaluar el cortocircuito de izquierda a derecha, como se describe en la documentación, no es exacto decir que siempre puede hacerlo. Hay dos casos demostrados en esta página en los que no es cierto, y ninguno de los errores se ha corregido en ninguna versión pública de SQL Server.EDITAR aquí es otro caso (necesito dejar de hacerlo) en el que una
CASE
expresión no se evalúa en el orden que esperaría, aunque no haya agregados involucrados.fuente
CASE
eso que se solucionó en silencioMi opinión sobre esto es que la documentación deja bastante claro que la intención es que CASE deba cortocircuitar. Como Aaron menciona, ha habido varios casos (¡ja!) En los que se ha demostrado que esto no siempre es cierto.
Hasta ahora, todos estos han sido reconocidos como errores y corregidos, aunque no necesariamente en una versión de SQL Server que puede comprar y parchear hoy (el error de plegado constante aún no ha llegado a una actualización acumulativa AFAIK). El nuevo error potencial, originalmente informado por Itzik Ben-Gan, aún no se ha investigado (Aaron o yo lo agregaremos a Connect en breve).
En relación con la pregunta original, hay otros problemas con CASE (y, por lo tanto, COALESCE) en los que se utilizan funciones de efecto secundario o subconsultas. Considerar:
El formulario COALESCE a menudo devuelve NULL, más detalles en https://connect.microsoft.com/SQLServer/feedback/details/546437/coalesce-subquery-1-may-return-null
Los problemas demostrados con las transformaciones del optimizador y el seguimiento de expresiones comunes significan que es imposible garantizar que CASE se cortocircuite en todas las circunstancias. Puedo concebir casos en los que tal vez ni siquiera sea posible predecir el comportamiento al inspeccionar el resultado del plan de exhibición pública, aunque hoy no tengo una réplica para eso.
En resumen, creo que puede estar razonablemente seguro de que CASE provocará un cortocircuito en general (particularmente si una persona razonablemente experta inspecciona el plan de ejecución, y ese plan de ejecución se 'aplica' con una guía de plan o sugerencias) pero si necesita una garantía absoluta, debe escribir SQL que no incluya la expresión en absoluto.
No es un estado de cosas enormemente satisfactorio, supongo.
fuente
Me he encontrado con otro caso donde
CASE
/COALESCE
no cortocircuito. El siguiente TVF generará una infracción de PK si se pasa1
como parámetro.Si se llama de la siguiente manera
O como
Ambos dan el resultado
mostrando que la
SELECT
(o al menos la población variable de la tabla) todavía se lleva a cabo y genera un error a pesar de que nunca se debe alcanzar esa rama de la declaración. El plan para laCOALESCE
versión está abajo.Esta reescritura de la consulta parece evitar el problema
Que da plan
fuente
Otro ejemplo
La consulta
No muestra lecturas en contra
T2
en absoluto.La búsqueda de
T2
está bajo un predicado de paso y el operador nunca se ejecuta. PeroHace espectáculo que
T2
se lee. A pesar deT2
que nunca se necesita ningún valor de .Por supuesto, esto no es realmente sorprendente, pero pensé que valía la pena agregarlo al repositorio de ejemplos de contador solo porque plantea el problema de lo que significa cortocircuito incluso en un lenguaje declarativo basado en conjuntos.
fuente
Solo quería mencionar una estrategia que quizás no hayas considerado. Puede que no coincida aquí, pero a veces resulta útil. Vea si esta modificación le brinda un mejor rendimiento:
Otra forma de hacerlo podría ser esto (básicamente equivalente, pero le permite acceder a más columnas de la otra consulta si es necesario):
Básicamente, esta es una técnica de unión "dura" de tablas, pero que incluye la condición de cuándo se debe unir cualquier fila. En mi experiencia, esto realmente ha ayudado a los planes de ejecución a veces.
fuente
No, no lo haría. Solo se ejecutará cuandoc.FirstName
seaNULL
.Sin embargo, deberías probarlo tú mismo. Experimentar. Dijiste que tu subconsulta es larga. Punto de referencia. Saca tus propias conclusiones sobre esto.La respuesta de @Aaron en la subconsulta que se ejecuta es más completa.
Sin embargo, sigo pensando que debería volver a trabajar su consulta y usarla
LEFT JOIN
. La mayoría de las veces, las subconsultas pueden eliminarse modificando su consulta para usarLEFT JOIN
s.El problema con el uso de subconsultas es que su declaración general se ejecutará más lentamente porque la subconsulta se ejecuta para cada fila en el conjunto de resultados de la consulta principal.
fuente
El estándar real dice que todas las cláusulas WHEN (así como la cláusula ELSE) deben analizarse para determinar el tipo de datos de la expresión como un todo. Realmente tendría que sacar algunas de mis notas anteriores para determinar cómo se maneja un error. Pero de la mano, 1/0 usa números enteros, por lo que supongo que si bien es un error. Es un error con el tipo de datos entero. Cuando solo tiene nulos en la lista de fusión, es un poco más complicado determinar el tipo de datos, y ese es otro problema.
fuente