SQL Server en la práctica

Índices de cobertura SQL Server: claves, INCLUDE y lookups

Diseña índices de cobertura según filtros y orden reales, y compara el ahorro de lookups con el costo de almacenamiento y escrituras.

Un plan puede mostrar Index Seek y aun así ser lento. La búsqueda puede identificar miles de filas y efectuar después un lookup en el índice agrupado para recuperar las columnas ausentes. El problema no consiste en tener o no un seek, sino en el trabajo adicional necesario para completar la consulta.

Un índice cubre una consulta concreta cuando contiene las columnas que esta necesita. La cobertura es una relación entre índice y consulta. Añadir una columna al SELECT o modificar un predicado puede hacer que deje de cubrirla. Empieza por la instrucción exacta y por los parámetros representativos de la aplicación.

Ordenar la clave para navegar

El ejemplo restringe un cliente por igualdad, aplica un rango de fechas y devuelve los pedidos más recientes. CustomerId encabeza la clave, seguido de OrderedAt y SaleId para resolver empates. Total y Status participan en la salida, pero no en el orden requerido, por lo que se incluyen mediante INCLUDE.

CREATE TABLE #Sales
(
    SaleId bigint NOT NULL PRIMARY KEY,
    CustomerId int NOT NULL,
    OrderedAt datetime2(0) NOT NULL,
    Total decimal(12,2) NOT NULL,
    Status tinyint NOT NULL
);
INSERT #Sales VALUES
(1,7,'2021-10-01',90,1),(2,7,'2021-10-02',40,0),
(3,8,'2021-10-02',20,1),(4,7,'2021-10-03',70,1);

CREATE INDEX IX_Sales_Customer_Date
ON #Sales(CustomerId, OrderedAt DESC, SaleId DESC)
INCLUDE(Total, Status);

DECLARE @CustomerId int=7;
DECLARE @From datetime2(0)='2021-10-01';
SELECT TOP (2) SaleId, OrderedAt, Total, Status
FROM #Sales
WHERE CustomerId=@CustomerId AND OrderedAt>=@From
ORDER BY OrderedAt DESC, SaleId DESC;

SET STATISTICS IO, TIME ON;
SELECT SaleId, OrderedAt, Total, Status
FROM #Sales
WHERE CustomerId=@CustomerId AND OrderedAt>=@From;
SET STATISTICS IO, TIME OFF;
DROP TABLE #Sales;

Para el cliente 7, la primera consulta devuelve 4 y 2. El índice puede resolver cliente y ordenación. Si la fecha estuviera primero, SQL Server podría recorrer el rango temporal, pero quizá tendría que leer numerosos clientes adicionales. Por eso, colocar siempre la columna globalmente más selectiva primero es una regla incompleta.

Una condición de rango suele limitar cuánto pueden restringir la búsqueda las columnas siguientes. Estas todavía pueden ayudar a ordenar o cubrir. Revisa los predicados reales de búsqueda y los filtros residuales. Compara filas leídas y devueltas, en lugar de deducir eficiencia únicamente por los nombres de las columnas.

Resolver un costo medido con INCLUDE

Las columnas incluidas se almacenan en el nivel hoja del índice no agrupado y no determinan su navegación. Pueden eliminar lookups, pero ensanchan páginas, consumen caché y aumentan almacenamiento y copias de seguridad. También se mantienen cuando sus valores cambian. Incluir un estado actualizado constantemente puede añadir bastante trabajo de escritura.

No copies automáticamente todas las columnas de una recomendación de índice faltante. Estas sugerencias reflejan una visión limitada del costo estimado y no consolidan los requisitos del conjunto de consultas. Revisa los índices existentes. Una ampliación moderada puede atender varias instrucciones, aunque diferentes órdenes de clave pueden seguir siendo necesarios.

Un lookup no es necesariamente un defecto. Diez búsquedas adicionales para diez filas pueden costar menos que mantener permanentemente un índice ancho de poco uso. El punto de cambio depende de cantidad de filas, caché, anchura y alternativas. Si un cliente tiene diez pedidos y otro un millón, ambos deben formar parte de la evaluación.

Probar el patrón completo

La tabla temporal comprueba los resultados esperados; no demuestra una mejora de rendimiento. Para eso usa datos representativos y captura STATISTICS IO, tiempo transcurrido, CPU y planes reales. Prueba clientes y rangos estrechos y amplios. No vacíes las cachés de producción para fabricar una prueba fría; compara ejecuciones equivalentes.

Incluye inserciones, correcciones de importes, cambios de estado y procesos de retención. Si desaparece una ordenación, compara asignaciones de memoria y derrames a tempdb. Si una consulta amplia sigue eligiendo un scan, puede ser la opción correcta. Forzar un seek con cientos de miles de lookups puede resultar peor.

La decisión debe identificar qué consultas mejoran, qué índice existente podría sobrar y cuánto cuestan las escrituras y el almacenamiento adicionales. Conserva la definición anterior y observa un ciclo normal de negocio. El índice de cobertura adecuado reduce el costo total de la carga, no solamente mejora el icono mostrado en el plan.

Referencias técnicas: Microsoft Learn: Included columns · Microsoft Learn: Index design.

Pregunta sobre este artículo

¿Tiene alguna pregunta sobre este tema?

Cuéntenos qué está evaluando o dónde tiene dificultades. Le responderemos con una recomendación práctica.

Inquiries are not enabled in this preview.

Hacer una pregunta sobre este artículo