Tengo una tabla, CustPassMaster
con 16 columnas, una de las cuales es CustNum varchar(8)
, y creé un índice IX_dbo_CustPassMaster_CustNum
. Cuando ejecuto mi SELECT
declaración:
SELECT * FROM dbo.CustPassMaster WHERE CustNum = '12345678'
Ignora el índice por completo. Esto me confunde ya que tengo otra tabla CustDataMaster
con 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: CustPassMaster
tiene 4991 registros y CustDataMaster
5376 registros. ¿Podría ser este el razonamiento detrás de ignorar el índice? CustPassMaster
también tiene registros duplicados que tienen los mismos CustNum
valores 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 SELECT
declaraciones. No hago ninguna declaración INSERT
/ UPDATE
/ DELETE
en él, excepto cuando la aplicación de copia de seguridad está copiando datos del AS / 400.
fuente
Respuestas:
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 *
, simplementeSELECT 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 losWHERE
criterios 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.fuente
INCLUDE
cláusula del índice?INCLUDE
clá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.Table1
y 0.53 KB paraTable2
. 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.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 on
y 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).
fuente
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.
fuente