2009-07-09 11 views
6

Tengo una pregunta bastante simple:SQL Server no utilizará mi índice

SELECT 
    col1, 
    col2… 
FROM 
    dbo.My_Table 
WHERE 
    col1 = @col1 AND 
    col2 = @col2 AND 
    col3 <= @col3 

Se estaba realizando horrible, por lo que añade un índice en col1, col2, col3 (int, poco, y de fecha y hora). Cuando revisé el plan de consulta estaba ignorando mi índice. Intenté reordenar las columnas en el índice en todas las configuraciones posibles y siempre ignoré el índice. Cuando ejecuto la consulta, realiza un análisis de índice agrupado (el tamaño de la tabla está entre 700K y 800K filas) y demora de 10 a 12 segundos. Cuando lo obligo a usar mi índice, regresa al instante. Tuve el cuidado de borrar el caché y los búferes entre las pruebas.

Otras cosas que he intentado:

UPDATE STATISTICS dbo.My_Table 

CREATE STATISTICS tmp_stats ON dbo.My_Table (col1, col2, col3) WITH FULLSCAN 

Me estoy perdiendo algo aquí? Odio poner una sugerencia de índice en un procedimiento almacenado, pero SQL Server parece que no puede obtener una pista sobre este. ¿Alguien sabe alguna otra cosa que pueda evitar que SQL Server reconozca que usar el índice es una buena idea?

EDIT: Una de las columnas que se devuelve es una columna TEXT, así que usar un índice de cobertura o un INCLUDE no funcionará :(

+1

Pone una elipsis en su lista de columnas, ¿qué otras columnas está seleccionando? ¿Y si solo fuera col1, col2 y col3? –

+0

Chris, buen punto, pero "cuando lo obligo a usar mi índice vuelve al instante" cubre eso. –

+0

Las elipsis se deben a que la consulta devuelve todas las columnas de la tabla. Supongo que podría haber puesto SELECT * –

Respuesta

13

Tiene 800k filas indexados por col1, col2, col3. Col2 es un poco, por lo que su selectividad es del 50%. Col3 se verificó en un rango (< =), por lo que su selectividad será aproximadamente del 50% también. Que deja col1. La consulta se compila para el plan genérico, parametrizado, por lo que debe tener en cuenta el caso general. Si tiene 10 valores distintos de col1, su índice devolverá aproximadamente 800k/10 * 25% que son aproximadamente ~ 20k claves para buscar en el índice agrupado para recuperar la parte '...'. Si tiene 10k valores distintos de col1, el índice devolverá solo 20 claves para buscar. Como puede ver, lo que importa no es cómo construye su índice en este caso, sino los datos reales. En función de la selectividad de col1, el optimizador elegirá un plan basado en un análisis de índice agrupado (como mejor que búsquedas de claves de 20k, cada búsqueda a un costo de al menos lecturas de 3-5 páginas) o uno basado en no índice agrupado (si col1 es lo suficientemente selectivo). En la vida real, la distribución de col1 también juega un papel, pero entrar en eso complicaría demasiado la explicación.

Puede venir con el beneficio de la retrospectiva y reclamar que el plan es incorrecto, pero el plan es la mejor estimación de costos en función de los datos disponibles en tiempo de compilación. Puede influir en ello con pistas (sugerencia de índice tal como sugiere u optimizar para sugerencias como lo sugiere Quassnoi) pero luego su consulta puede tener un mejor rendimiento para su conjunto de prueba, y mucho peor para un conjunto diferente de datos, por ejemplo para el caso cuando @ col1 = <the value that matches 500k records>. También puede hacer que el índice cubra, eliminando así el '...' en la lista de proyección que requiere la búsqueda de índice agrupado necesaria, en cuyo caso el índice no agrupado es siempre una mejor coincidencia de costos que el análisis agrupado.

Kimberley Tripp tiene un artículo de blog que cubre este tema, se lo llama el 'index tipping point' que explica cómo es que un índice candidato aparentemente perfecta está siendo ignorada: un índice no agrupado que no cubre la lista de proyección y tiene mala la selectividad se verá como más costosa que una exploración en clúster.

+0

Gracias por la información y las sugerencias. Me da algunas ideas para investigar. –

1

El orden del índice es importante para esta consulta:

CREATE INDEX MyIndex ON MyTable (col3 DESC, col2 ASC, col1 ASC) 

está no es tanto la ASC/DESC como que cuando SQL Server va a coincide con la cláusula where, que puede coincidir con el col3 primero y caminar el índice lo largo de ese valor.

+1

Para esta consulta, el orden del índice debería ser tal como @Tom H. lo creó. – Quassnoi

+0

Probé varios pedidos posibles para las columnas. Todos dieron el mismo resultado. –

2

SQL Server optimizador no es bueno en O ptimizing consultas que usan variables.

Si está seguro de que siempre se beneficiará con el uso del índice, solo haga una sugerencia.

Si va a poner los valores literales a la consulta en lugar de las variables, seleccionará las estadísticas correctas y usará el índice.

También puede tratar de poner un toque más luz:

OPTION (OPTIMIZE FOR (@col1 = 1, @col2 = 0, @col3 = '2009-07-09')) 

, que calculará el mejor plan de ejecución de estos valores de las variables, utilizando las estadísticas, y no se pegue a la utilización de índice no importa qué.

+0

Si ejecuto la consulta fuera del SP, con los valores de columna codificados, todavía utiliza un análisis de índice agrupado :( –

+0

@Tom: ¿podría publicar la definición exacta de la tabla? – Quassnoi

1

¿Has intentado tirar la broca del índice?

create index ix1 on My_Table(Col3, Col1) INCLUDE(Col2) 
-- include other columns from the select list if needed 

Además, ha omitido el resto de las columnas de la lista de selección. Es posible que desee considerar incluirlos si no hay muchos en el índice o en la instrucción INCLUDE para crear un índice de cobertura para la consulta.

1

Trate de enmascarar sus parámetros para evitar la inhalación Parámetro:

CREATE PROCEDURE MyProc AS 
    @Col1 INT 
    -- etc... 
AS 
    DECLARE @MaskedCol1 INT 
    SET @MaskedCol1 = @Col1 
    -- etc... 

    SELECT 
     col1, 
     col2… 
    FROM 
     dbo.My_Table 
    WHERE 
     col1 = @MaskecCol1 AND 
     -- etc... 

suena estúpido pero he visto servidor SQL hacer algunas cosas extrañas a causa de descubrimiento de parámetros.

+0

Gracias por la sugerencia.Sin embargo, puedo ejecutar SELECT fuera del SP y todavía veo la misma situación. –

1

Apuesto a que SQL Server piensa que el precio de obtener el resto de las columnas (designadas por ... en su ejemplo) del índice agrupado supera el beneficio del índice, de modo que solo escanea la clave agrupada. Si es así, vea si puede hacer que este sea un índice de cobertura.

¿O usa otro índice en su lugar?

+0

Está utilizando la clave agrupada si no fuerzo el uso del índice. La lista de columnas incluye todas las columnas en la tabla. Si bien podría poner un gran índice de cobertura sobre eso, estaría duplicando efectivamente la mesa. Tendré que buscar en las frecuencias INSERT/UPDATE/DELETE para ver si el costo está garantizado. –

0

¿Las columnas son nulables? A veces el Servidor SQL piensa que tiene que escanear la tabla para encontrar valores NULL.

Intenta agregar "y col1 no es nulo" a la consulta, simplemente haga que sqlserver use el índice sin sugerencia.

Además, verifique si las estadísticas son realmente hasta la fecha:

SELECT 
    object_name = Object_Name(ind.object_id), 
    IndexName = ind.name, 
    StatisticsDate = STATS_DATE(ind.object_id, ind.index_id) 
FROM SYS.INDEXES ind 
order by STATS_DATE(ind.object_id, ind.index_id) desc 
+0

A diferencia de Oracle, SQL Server indexa los NULL igual de bien y un índice siempre cubre todas las filas. – Quassnoi

+0

Mi teoría era que a veces las estadísticas muestran que hay muchas filas con NULLs. Luego, el servidor Sql vuelve a una exploración de tabla para cubrir el caso "c1 es nulo". – Andomar

+0

@Andomar: esta consulta nunca puede cubrir las filas para las cuales 'COL1' es' NULL' – Quassnoi

0

Si su SELECT devuelve columnas que no están en su SQL de índice, creo que es más eficiente escanear el índice agrupado en lugar de tener que hacer una búsqueda de claves para encontrar los otros valores que está solicitando.

Si tiene una columna TEXT intente cambiar el tipo de datos a VARCHAR (MAX) y luego incluir los valores en el índice no agrupado.

Cuestiones relacionadas