2010-01-29 13 views
5

Me pregunto si existe alguna forma inteligente de reescribir la siguiente consulta para que el optimizador utilice los índices en las columnas.Cómo optimizar el uso de la cláusula "O" cuando se usa con parámetros (SQL Server 2008)

Create Procedure select_Proc1 
    @Key1 int=0, 
    @Key2 int=0 
As 
BEGIN 
    Select key3 
    From Or_Table 
    Where (@key1 =0 OR Key1 [email protected]) AND 
      (@key2 =0 OR Key2 [email protected]) 
END 
GO 

A pesar de que las columnas en las cláusulas WHERE están cubiertos por índices, SQL Server no puede utilizar estos índices. Esto plantea la pregunta de si algo está "bloqueando" el uso de los índices. La respuesta a esta pregunta es sí: los culpables son los parámetros y la condición "O". Los parámetros no están cubiertos por índices, lo que significa que SQL Server no puede usar ninguno de los índices para evaluar "@ key1 = 0" (una condición que también se aplica a @ key2 = 0). Efectivamente, esto significa que SQL Server no puede usar índices para evaluar la cláusula "@ key1 = 0 OR Key1 = @ key1" (como la cláusula "OR" es la unión de filas cubiertas por ambas condiciones). El mismo principio se aplica a la otra cláusula (re. Key2) también. Esto lleva a SQL Server a concluir que no se pueden usar índices para extraer las filas, dejando que SQL Server utilice el siguiente mejor enfoque: un análisis de índice agrupado

Como ve, el optimizador de SQL no usará índices en las columnas si los predicados son "OR" ed en la cláusula WHERE. Una solución para este problema es separar las consultas con la cláusula IF para todas las combinaciones posibles de parámetros.

Lea este breve artículo para obtener una mejor vista del problema: http://www.sql-server-performance.com/articles/per/optimize_or_clause_p1.aspx

Ahora mi pregunta es, ¿qué debemos hacer si las posibles combinaciones son más que sólo tres o cuatro? Escribir una consulta separada para cada combinación no parece una solución racional. ¿Hay alguna otra solución para este problema?

Respuesta

11

SQL Server no es muy bueno en la optimización de los predicados OR.

Utilice esta:

SELECT key3 
FROM or_table 
WHERE @key1 = 0 
     AND @key2 = 0 
UNION ALL 
SELECT key3 
FROM or_table 
WHERE @key1 = 0 
     AND @key2 <> 0 
     AND key2 = @key2 
UNION ALL 
SELECT key3 
FROM or_table 
WHERE @key2 = 0 
     AND @key1 <> 0 
     AND key1 = @key1 
UNION ALL 
SELECT key3 
FROM or_table 
WHERE @key1 <> 0 
     AND @key2 <> 0 
     AND key1 = @key1 
     AND key2 = @key2 

SQL Server buscarán los valores de las variables antes de ejecutar las consultas y optimizará las consultas redundantes a cabo.

Esto significa que solo se ejecutará una consulta de cuatro.

+0

enfoque interesante, voy a tener que recordarlo. Supongo que también se aplicaría a otros RDBMS como Oracle, donde 'OR 'es terriblemente ineficiente. – Lucero

+1

¿Es esta la única manera? ¿Qué pasa si la consulta es más complicada (más cláusulas de OR involucradas) – Meysam

3

MSSQL 2008 tiene una sintaxis optimización de la condición de la simplificación, aquí está

Where (@key1 =0 OR Key1 [email protected]) AND 
     (@key2 =0 OR Key2 [email protected]) option(recompile) 

Esto optimizará el uso de constantes

+0

No creo que esto ayude. Solo funcionaría si se tratara de un problema de detección de parámetros. – Meysam

2

¿Te intenta una tabla de función valorada?

CREATE FUNCTION select_func1 ( 
    @Key1 int=0, 
    @Key2 int=0 
) 
RETURNS TABLE 
AS RETURN (
    Select key3 
    From Or_Table 
    Where (@key1 =0 OR Key1 [email protected]) AND 
      (@key2 =0 OR Key2 [email protected]) 
) 


select * from select_func1(1,2) 
+0

¡OMG! Como puedo ver en el plan de ejecución, ¡los índices se usan cuando se usa la función de valores de tabla! ¡¿Cuál es la diferencia?! ¿Podrías explicarlo? – Meysam

+0

Una función con valores de tabla se evalúa cada vez que se usa (como una vista). Esto significa que el optimizador podrá evaluar los parámetros antes de crear el plan de consulta. Puede ver las funciones con valores de tabla como macros de texto avanzado en la consulta externa. – adrianm

+0

Vaya, todavía hay un problema con su enfoque. Si reemplaza los parámetros constantes en la llamada a función con parámetros variables, nuevamente los índices no se utilizan. algo como esto: seleccione * de select_func1 (@ key1, @ key2) – Meysam

0

Sí - cuidado uso de SQL dinámico va a resolver este problema. Hay dos formas de hacerlo:

a. Si eres un "purista" sobre los procesos almacenados, entonces crea una cadena de consulta personalizada dentro de un proceso almacenado y ejecuta la cadena. La consulta específica puede escribirse dinámicamente por ejecución para incluir solo los criterios relevantes.

b. Si es flexible con respecto a la ubicación de este SQL, puede (nuevamente CUIDADOSAMENTE) componer la cadena de consulta en su aplicación y pasarla al servidor.

El peligro, por supuesto, es alrededor de la inyección SQL. Por lo tanto, debe tener mucho cuidado con la forma en que los datos pasan del cliente a la declaración dinámica de sql.

Información completa de Erland Sommarskog: http://www.sommarskog.se/dynamic_sql.html y http://www.sommarskog.se/dyn-search.html

Cuestiones relacionadas