Expresar la consulta usando una sintaxis diferente a veces puede ayudar a comunicar su deseo de usar un índice no agrupado al optimizador. Debe encontrar el formulario a continuación que le brinda el plan que desea:
SELECT
[ID],
[DeviceID],
[IsPUp],
[IsWebUp],
[IsPingUp],
[DateEntered]
FROM [dbo].[Heartbeats]
WHERE
[ID] IN
(
-- Keys
SELECT TOP (1000)
[ID]
FROM [dbo].[Heartbeats]
WHERE
[DateEntered] >= CONVERT(datetime, '2011-08-30', 121)
AND [DateEntered] < CONVERT(datetime, '2011-08-31', 121)
);
Compare ese plan con el producido cuando el índice no agrupado se fuerza con una pista:
SELECT TOP (1000)
*
FROM [dbo].[Heartbeats] WITH (INDEX(CommonQueryIndex))
WHERE
[DateEntered] BETWEEN '2011-08-30' and '2011-08-31';
Los planes son esencialmente los mismos (una búsqueda clave no es más que una búsqueda en el índice agrupado). Ambas formas de plan solo realizarán una búsqueda en el índice no agrupado y un máximo de 1000 búsquedas en el índice agrupado.
La diferencia importante está en la posición del operador superior. Posicionado entre las dos búsquedas, la parte superior evita que el optimizador reemplace las dos operaciones de búsqueda con un escaneo lógicamente equivalente del índice agrupado. El optimizador funciona reemplazando partes de un plan lógico con operaciones relacionales equivalentes. Top no es un operador relacional, por lo que la reescritura evita la transformación a un escaneo de índice agrupado. Si el optimizador pudiera reposicionar el operador Top, aún preferiría el escaneo a la búsqueda + búsqueda debido a la forma en que funciona la estimación de costos.
Costeo de escaneos y búsquedas
En un nivel muy alto, el modelo de costo del optimizador para escaneos y búsquedas es bastante simple: estima que 320 búsquedas aleatorias cuestan lo mismo que leer 1350 páginas en un escaneo. Esto probablemente se parece poco a las capacidades de hardware de cualquier sistema de E / S moderno en particular, pero funciona razonablemente bien como modelo práctico.
El modelo también hace una serie de suposiciones simplificadoras, una de las cuales es que se supone que cada consulta comienza sin datos o páginas de índice ya en caché. La implicación es que cada E / S dará como resultado una E / S física, aunque esto rara vez será el caso en la práctica. Incluso con un caché frío, la búsqueda previa y la lectura anticipada significan que las páginas necesarias en realidad es probable que estén en la memoria cuando el procesador de consultas las necesite.
Otra consideración es que la primera solicitud de una fila que no está en la memoria hará que toda la página se recupere del disco. Las solicitudes posteriores de filas en la misma página probablemente no incurrirán en una E / S física. El modelo de costeo contiene lógica para tener en cuenta efectos como este, pero no es perfecto.
Todas estas cosas (y más) significa que el optimizador tiende a cambiar a un escaneo antes de lo que probablemente debería. La E / S aleatoria es solo "mucho más costosa" que la E / S "secuencial" si se produce una operación física: el acceso a las páginas en la memoria es realmente muy rápido. Incluso cuando se requiere una lectura física, un escaneo puede no resultar en lecturas secuenciales debido a la fragmentación, y las búsquedas pueden ser colocadas de manera tal que el patrón sea esencialmente secuencial. Agregue a eso la característica de rendimiento cambiante de los sistemas modernos de E / S (especialmente de estado sólido) y todo comienza a verse muy inestable.
Objetivos de fila
La presencia de un operador Top en un plan modifica el enfoque de costos. El optimizador es lo suficientemente inteligente como para saber que encontrar 1000 filas usando un escaneo probablemente no requerirá escanear todo el índice agrupado; puede detenerse tan pronto como se hayan encontrado 1000 filas. Establece un 'objetivo de fila' de 1000 filas en el operador Superior y usa información estadística para trabajar desde allí para estimar cuántas filas espera necesitar del origen de la fila (una exploración en este caso). Escribí sobre los detalles de este cálculo aquí .
Las imágenes de esta respuesta se crearon con el Explorador de planes SQL Sentry .