¿Forzar a SQL Server a ejecutar condiciones de consulta tal como están escritas?

14

Estoy usando SQL Server 2008 R2 y tengo esta pseudo consulta (SP):

select ...
from ...
WHERE    @LinkMode IS NULL
     AND (myColumn IN (...very long-running query...))
     ...
     ...

El problema es que la consulta tarda mucho tiempo en ejecutarse, incluso si ejecuto el SP con @LinkMode=2.

Como notó, la consulta de larga duración debe ejecutarse solo si @LinkMode es nulo, que no es el caso aquí. En mi caso @LinkMode = 2!

Sin embargo, si lo cambio a:

 select ...
    from ...
    WHERE    1=2
         AND (myColumn IN (...very long time exeted query...))
     ...
     ...

el SP no correr rápido.

He escuchado antes que a veces el optimizador puede optimizar el orden de los criterios.

Entonces pregunto :

  • Incluso si el optimizador elige una ruta diferente, ¿qué puede ser más rápido que verificar si =null? Quiero decir, creo que verificar if a==nulles mucho más rápido que ejecutar la otra consulta larga ...

  • ¿Cómo puedo forzar a SQL Server a ejecutar la consulta tal como la he escrito (el mismo orden)?

Royi Namir
fuente

Respuestas:

22

Estás cayendo en la trampa " Catch-All Query ", que Gail Shaw explica muy bien aquí .

Para resumir el problema: SQL Server optimiza la sobrecarga significativa de la compilación de consultas almacenando en caché un plan de consulta después de la compilación y luego verificando la caché para un plan de consulta coincidente antes de una compilación posterior. La "coincidencia" que ocurre aquí es puramente textual, por lo que el valor real de una variable no afectará esto.

Eso es bueno el 99% del tiempo, pero en algunos casos es malo . Un caso en el que es malo es cuando alguien intenta construir una cláusula WHERE como si fuera una declaración IF de cortocircuito en C, etc. Esto no funciona bien, porque el compilador SQL tiene que hacer un plan de consulta que funcione independientemente de cuáles son realmente los valores de los parámetros, y la única forma en que puede manejar estas condiciones de conmutación lógicas "inteligentes" en la cláusula WHERE es hacer un plan simple de fuerza bruta que simplemente escanee toda la tabla, filtrando las filas a medida que avanza , sin apalancar ningún índice.

No es sorprendente que esto los haga uniformemente lentos, sin importar cuáles sean los valores de los parámetros / variables.

RBarryYoung
fuente
8

No hay una forma garantizada de forzar al servidor SQL a ejecutar las condiciones de su cláusula en una secuencia específica. El optimizador siempre los evaluará en el orden que considere adecuado.

Lo que puedes hacer es algo como esto:

IF @LinkMode IS NULL
BEGIN
    select ...
    from ...
    WHERE (myColumn IN (...very long time exeted query...))
         ...
         ...
END
ELSE
BEGIN
    select ...
    from ...
    WHERE ...
         ...
END
Nombre de pantalla esotérico
fuente
3

Si es una opción, use una instrucción IF para ejecutar la forma apropiada de la consulta. Además, en SQL, le dice al motor de db qué hacer, no cómo hacerlo: las cosas no se ejecutan de principio a fin. Puede ser difícil predecir qué hará exactamente. Probablemente ya sepas esto;)

Sam
fuente
2

El SQL dinámico probablemente también funcionaría, ya que en ese caso el optimizador de consultas debería obtener los valores reales en tiempo de ejecución (corríjame si estoy equivocado, en realidad no estoy seguro, pero parece recordar haberlo usado para situaciones similares) . Pero estoy con los demás en este caso, ya que una cláusula IF / ELSE le serviría mejor, ya que es la solución más simple y fácil que hará exactamente lo que se necesita.

Para futuras referencias en caso de que aún no lo haya usado, puede encontrar aquí un sitio horriblemente feo con un ejemplo de trabajo para SQL dinámico: http://sqlusa.com/bestpractices/dynamicsql/

Kahn
fuente
1

Recomendaría la construcción IF / ELSE. Si por alguna razón no funciona para usted, siempre puede considerar usar la opción WITH RECOMPILE.

timvw
fuente
¿Podrías explicar cómo sería la "construcción if / else"? : D
jcolebrand
Iba a sugerir usar OPTION (WITH RECOMPILE), ya que eso generaría un plan ideal cada vez: el retraso de compilación agregaría una sobrecarga, pero sospecho que es mejor en general en este caso.
SqlRyan