La Batalla:
Seek vs Scan
El 80% de las consultas lentas se resuelven diseñando el índice correcto. En este lab, aprenderás a evitar los costosos Key Lookups usando índices "Covering".
El Problema de Negocio
Finanzas necesita un reporte en tiempo real: "Ingresos totales por cantidad de pasajeros". La consulta es simple, pero en la tabla de 1 millón de filas, bloquea el servidor.
SELECT PassengerCount, SUM(TotalAmount)
FROM Trips
GROUP BY PassengerCount;
Diagnóstico (Línea Base)
Antes de optimizar, debemos medir. Usamos STATISTICS IO para ver las "Lecturas Lógicas" (Páginas de 8KB leídas de memoria).
SET STATISTICS IO, TIME ON;
-- Ejecutamos sin índices Non-Clustered (solo tenemos el PK)
SELECT PassengerCount, SUM(TotalAmount)
FROM Trips
GROUP BY PassengerCount;
CPU time = 1200 ms, Elapsed time = 1250 ms.
Análisis del Plan: Clustered Index Scan
Como no hay un índice específico para PassengerCount, SQL Server tuvo que leer TODAS las páginas de la tabla (las 24,500 páginas que vimos en el Lab 1) para agrupar los datos. Esto es O(N). Inaceptable.
La Solución: Covering Index
Muchos crearían un índice solo en PassengerCount. ¡ERROR!
Si el índice no tiene el TotalAmount, el motor encuentra las filas rápido pero debe saltar a la tabla base para buscar el dinero. Eso es un Key Lookup y para agregaciones masivas es peor que un Scan.
TotalAmount en las hojas del índice. Así el motor nunca toca la tabla principal.
-- Creamos el índice perfecto para esta consulta
CREATE NONCLUSTERED INDEX IX_Pasajeros_Covering
ON Trips(PassengerCount) -- Columna del GROUP BY / WHERE
INCLUDE (TotalAmount); -- Columna del SELECT / SUM
-- Nota: TotalAmount NO es parte del árbol de búsqueda,
-- solo vive en las hojas como dato adjunto (payload).
La Prueba Final
Volvemos a ejecutar la misma consulta. El motor debería detectar automáticamente el nuevo índice y usarlo.
SELECT PassengerCount, SUM(TotalAmount)
FROM Trips
GROUP BY PassengerCount;
CPU time = 15 ms, Elapsed time = 15 ms.