¿Por qué SQL Server ignoraría un índice?


16

Tengo una tabla, CustPassMastercon 16 columnas, una de las cuales es CustNum varchar(8), y creé un índice IX_dbo_CustPassMaster_CustNum. Cuando ejecuto mi SELECTdeclaración:

SELECT * FROM dbo.CustPassMaster WHERE CustNum = '12345678'

Ignora el índice por completo. Esto me confunde ya que tengo otra tabla CustDataMastercon muchas más columnas (55), una de las cuales es CustNum varchar(8). Creé un índice en esta columna ( IX_dbo_CustDataMaster_CustNum) en esta tabla, y utilizo prácticamente la misma consulta:

SELECT * FROM dbo.CustDataMaster WHERE CustNum = '12345678'

Y usa el índice que creé.

¿Hay algún razonamiento específico detrás de esto? ¿Por qué usaría el índice de CustDataMaster, pero no el de CustPassMaster? ¿Se debe al bajo recuento de columnas?

La primera consulta devuelve 66 filas. Para el segundo, se devuelve 1 fila.

Además, nota adicional: CustPassMastertiene 4991 registros y CustDataMaster5376 registros. ¿Podría ser este el razonamiento detrás de ignorar el índice? CustPassMastertambién tiene registros duplicados que tienen los mismos CustNumvalores también. ¿Es este otro factor?

Estoy basando esta afirmación en los resultados del plan de ejecución real de ambas consultas.

Aquí está el DDL para CustPassMaster(el que tiene el índice no utilizado):

CREATE TABLE dbo.CustPassMaster(
    [CustNum] [varchar](8) NOT NULL,
    [Username] [char](15) NOT NULL,
    [Password] [char](15) NOT NULL,
    /* more columns here */
    [VBTerminator] [varchar](1) NOT NULL
) ON [PRIMARY]

CREATE NONCLUSTERED INDEX [IX_dbo_CustPassMaster_CustNum] ON dbo.CustPassMaster
(
    [CustNum] ASC
) WITH (PAD_INDEX = OFF
    , STATISTICS_NORECOMPUTE = OFF
    , SORT_IN_TEMPDB = OFF
    , DROP_EXISTING = OFF
    , ONLINE = OFF
    , ALLOW_ROW_LOCKS = ON
    , ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]

Y el DDL para CustDataMaster(he omitido muchos campos irrelevantes):

CREATE TABLE dbo.CustDataMaster(
    [CustNum] [varchar](8) NOT NULL,
    /* more columns here */
    [VBTerminator] [varchar](1) NOT NULL
) ON [PRIMARY]

CREATE NONCLUSTERED INDEX [IX_dbo_CustDataMaster_CustNum] ON dbo.CustDataMaster
(
    [CustNum] ASC
)WITH (PAD_INDEX = OFF
    , STATISTICS_NORECOMPUTE = OFF
    , SORT_IN_TEMPDB = OFF
    , DROP_EXISTING = OFF
    , ONLINE = OFF
    , ALLOW_ROW_LOCKS = ON
    , ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]

No tengo un índice agrupado en ninguna de esas tablas, solo un índice no agrupado.

Ignore el hecho de que los tipos de datos no coinciden completamente con el tipo de datos que se almacenan. Estos campos son una copia de seguridad de una base de datos IBM AS / 400 DB2, y estos son los tipos de datos compatibles. (Tengo que poder consultar esta base de datos de respaldo con las mismas consultas exactas y obtener exactamente los mismos resultados).

Estos datos solo se usan para SELECTdeclaraciones. No hago ninguna declaración INSERT/ UPDATE/ DELETEen él, excepto cuando la aplicación de copia de seguridad está copiando datos del AS / 400.


Puede valer la pena leer este artículo sobre el punto de inflexión de NonClustered a Clustered. sqlskills.com/blogs/kimberly/the-tipping-point-query-answers
Mark Sinkinson

3
Entonces esa es la diferencia. Si la primera consulta usara su índice, tendría que realizar 65 búsquedas. Esto es costoso. La segunda consulta solo tiene que realizar una.
Aaron Bertrand

Respuestas:


18

Normalmente, SQL Server usará los índices si considera que es más conveniente usar el índice que usar directamente la tabla subyacente.

Parece probable que el optimizador basado en costos piense que sería más costoso usar el índice en cuestión. Puede verlo usar el índice si en lugar de hacerlo SELECT *, simplemente SELECT T1Col1.

Cuando le SELECT *dice a SQL Server que devuelva todas las columnas de la tabla. Para devolver esas columnas, SQL Server debe leer las páginas de las filas que coinciden con los WHEREcriterios de la declaración de la tabla misma (índice agrupado o montón). SQL Server probablemente esté pensando que la cantidad de lecturas necesarias para obtener el resto de las columnas de la tabla significa que también podría escanear la tabla directamente. Sería útil ver la consulta real y el plan de ejecución real utilizado por la consulta.


3
Entonces, ¿una solución más obvia y óptima sería limitar las columnas que selecciono e incluirlas en la INCLUDEcláusula del índice?
Der Kommissar

1
Eso bien podría hacer una gran diferencia. Agregar todas las columnas devueltas por la consulta a la INCLUDEcláusula probablemente hará que SQL Server use el índice. Habiendo dicho eso, ¿qué estás tratando de optimizar? Me parece que si su tabla tiene un tamaño de fila promedio de 100 bytes, entonces 5000 filas son solo alrededor de 500kb de datos, y es posible que no valga la pena dedicarle tiempo.
Max Vernon

1
El tamaño de fila promedio es de 0.30 KB para Table1y 0.53 KB para Table2. Todos estos datos se importan de un AS / 400 (IBM System i) y NO hay PK en nada. Hoy creé manualmente todos los índices después de que la gente mencionara que la aplicación es bastante lenta a veces.
Der Kommissar

10

Para usar el índice, porque lo está haciendo select *, entonces SQL Server primero debe leer cada una de las filas del índice que coincidan con el valor que tiene en la cláusula where. En base a esto, obtendrá los valores del índice agrupado para cada una de las filas, y luego tendrá que buscar cada uno de ellos por separado del índice agrupado (= búsqueda de clave). Como usted dijo que los valores no son únicos, SQL Server usa estadísticas para estimar cuántas veces tiene que hacer esta búsqueda de claves.

Lo más probable es que el costo estimado para escanear el índice no agrupado + búsquedas de teclas exceda el costo estimado para el escaneo de índice agrupado, y es por eso que se ignora el índice.

Puede intentar usar set statistics io ony luego usar una pista de índice para ver si el costo de E / S es realmente menor cuando se usa el índice o no. Si la diferencia es grande, puede consultar las estadísticas, si están desactualizadas.

Además, si su SQL realmente está utilizando variables y no los valores exactos, esto también podría ser causado por la detección de parámetros (= el valor anterior utilizado para crear el plan tenía muchas filas en la tabla).


1

Esa podría ser la razón. Los optimizadores se basan en el costo y deciden qué ruta elegir en función del "costo" que tiene cada ruta de ejecución. El costo 'más grande' es llevar los datos del disco a la memoria. Si el optimizador calcula que lleva más tiempo leer tanto el índice como los datos, entonces podría decidir omitir el índice. Cuanto más grandes son las filas, más bloques de disco toman.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.