L'expression de la requête à l'aide d'une syntaxe différente peut parfois aider à communiquer votre souhait d'utiliser un index non clusterisé à l'optimiseur. Vous devriez trouver que le formulaire ci-dessous vous donne le plan que vous souhaitez:
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)
);

Comparez ce plan avec celui produit lorsque l'index non clusterisé est forcé avec un indice:
SELECT TOP (1000)
*
FROM [dbo].[Heartbeats] WITH (INDEX(CommonQueryIndex))
WHERE
[DateEntered] BETWEEN '2011-08-30' and '2011-08-31';

Les plans sont essentiellement les mêmes (une recherche de clé n'est rien de plus qu'une recherche sur l'index clusterisé). Les deux formulaires de plan n'effectueront qu'une seule recherche sur l'index non cluster et un maximum de 1 000 recherches dans l'index cluster.
La différence importante réside dans la position de l'opérateur Top. Positionné entre les deux recherches, le sommet empêche l'optimiseur de remplacer les deux opérations de recherche par une analyse logiquement équivalente de l'index clusterisé. L'optimiseur fonctionne en remplaçant des parties d'un plan logique par des opérations relationnelles équivalentes. Top n'est pas un opérateur relationnel, donc la réécriture empêche la transformation en une analyse d'index en cluster. Si l'optimiseur était en mesure de repositionner l'opérateur Top, il préférerait toujours le scan à la recherche + recherche en raison du fonctionnement de l'estimation des coûts.
Coût des analyses et des recherches
À un niveau très élevé, le modèle de coût de l'optimiseur pour les numérisations et les recherches est assez simple: il estime que 320 recherches aléatoires coûtent le même prix que la lecture de 1350 pages dans une numérisation. Cela ressemble probablement peu aux capacités matérielles d'un système d'E / S moderne particulier, mais il fonctionne raisonnablement bien en tant que modèle pratique.
Le modèle fait également un certain nombre d'hypothèses simplificatrices, la principale étant que chaque requête est supposée commencer sans données ni pages d'index déjà dans le cache. L'implication est que chaque E / S entraînera une E / S physique - bien que ce soit rarement le cas dans la pratique. Même avec un cache froid, la prélecture et la lecture anticipée signifient que les pages nécessaires sont en fait très probablement en mémoire au moment où le processeur de requêtes en a besoin.
Une autre considération est que la première demande pour une ligne qui n'est pas en mémoire provoquera l'extraction de la page entière du disque. Les demandes ultérieures de lignes sur la même page n'entraîneront très probablement pas d'E / S physiques. Le modèle de calcul des coûts contient une logique pour prendre en compte des effets comme celui-ci, mais il n'est pas parfait.
Toutes ces choses (et plus encore) signifient que l'optimiseur a tendance à passer à un scan plus tôt que probablement. Les E / S aléatoires ne sont «beaucoup plus chères» que les E / S «séquentielles» si une opération physique en résulte - l'accès aux pages en mémoire est en effet très rapide. Même lorsqu'une lecture physique est requise, une analyse peut ne pas donner lieu du tout à des lectures séquentielles en raison de la fragmentation, et les recherches peuvent être colocalisées de telle sorte que le motif est essentiellement séquentiel. Ajoutez à cela les caractéristiques de performance changeantes des systèmes d'E / S modernes (en particulier à semi-conducteurs) et le tout commence à sembler très fragile.
Objectifs de rang
La présence d'un opérateur Top dans un plan modifie l'approche des coûts. L'optimiseur est suffisamment intelligent pour savoir que la recherche de 1 000 lignes à l'aide d'une analyse ne nécessitera probablement pas l'analyse de l'index cluster complet - il peut s'arrêter dès que 1 000 lignes ont été trouvées. Il définit un `` objectif de ligne '' de 1000 lignes à l'opérateur supérieur et utilise des informations statistiques pour revenir en arrière à partir de là pour estimer le nombre de lignes dont il s'attend à avoir besoin de la source de ligne (une analyse dans ce cas). J'ai écrit sur les détails de ce calcul ici .
Les images de cette réponse ont été créées à l'aide de SQL Sentry Plan Explorer .