web-dev-qa-db-fra.com

Le plan d'exécution n'utilise pas l'index, il utilise une analyse de table

Je sais quand il s'agit d'utiliser un index ou une numérisation de table, SQL Server utilise des statistiques pour voir le meilleur.

J'ai une table avec 20 millions de rangées. J'ai un index sur (snapshotkey, mesure) et cette requête:

select Measure, SnapshotKey, MeasureBand
from t1
where Measure = 'FinanceFICOScore'
group by Measure, SnapshotKey, MeasureBand

La requête renvoie 500 000 rangées. La requête ne sélectionne donc que 2,5% des lignes de la table.

La question est de savoir pourquoi SQL Server n'utilise pas l'index non clusterné que j'ai et utilise une analyse de table à la place?

Les statistiques sont mises à jour.

C'est bien de mentionner que la performance de la requête est bonne cependant.

Scan de table

Table Scan

Index forcé

Force Index

Structure de table/index

CREATE TABLE [t1](
    [SnapshotKey] [int] NOT NULL,
    [SnapshotDt] [date] NOT NULL,
    [Measure] [nvarchar](30) NOT NULL,
    [MeasureBand] [nvarchar](30) NOT NULL,
    -- and many more fields
) ON [PRIMARY]

Pas de pk sur la table, comme il s'agit d'un entrepôt de données.

CREATE NONCLUSTERED INDEX [nci_SnapshotKeyMeasure] ON [t1]
(
    [SnapshotKey] ASC,
    [Measure] ASC
)
9
user71787

La recherche d'index pourrait ne pas être le meilleur choix si vous retournez de nombreuses lignes et/ou que les rangées sont très larges. Les recherches peuvent être chères si votre indice ne couvre pas. voir n ° 2 ici .

Dans votre scénario, l'optimisation de la requête estime que 50 000 recherches individuelles seront plus chères qu'une seule analyse. Le choix de l'optimiseur entre scanner et rechercher (avec des recherches débarras pour les colonnes nécessaires à la requête, mais pas présent dans l'index non clusterisé) est basé sur le coût estimé de chaque alternative.

L'optimiseur choisit toujours l'alternative de coût la plus faible qu'elle considère. Si vous regardez la propriété (=== --- ==) dans le nœud racine des deux plans d'exécution, vous verrez que le plan de numérisation a un coût estimé inférieur à celui de le plan de recherche. En conséquence, l'optimiseur a choisi l'analyse. C'est essentiellement la réponse à votre question.

Maintenant, le modèle de coût utilisé par l'optimiseur est basé sur des hypothèses et des "numéros magiques" qui sont tout à fait improbables pour correspondre aux caractéristiques de performance de votre système. En particulier, une hypothèse faite dans le modèle est que la requête commence à exécuter avec aucune des données ou des pages d'index requises déjà en mémoire. Un autre est que les E/S séquentielles (attendu pour une analyse) est moins chère que le motif d'E/S aléatoire supposé pour les recherches RID. Il y a beaucoup d'autres hypothèses et mises en garde, beaucoup trop nombreuses pour entrer en détail ici.

Néanmoins, le modèle de coût dans son ensemble a été démontré que les plans "suffisamment bons" pour la plupart des requêtes, sur la plupart des schémas de base de données, sur la plupart des configurations matérielles. , la plupart du temps, partout. C'est tout à fait une réussite, si vous y réfléchissez.

Les limitations de modèle et d'autres facteurs signifieront parfois que l'optimiseur choisit un plan qui n'est pas, en fait, "assez bon" du tout. Vous rapportez que "la performance est bonne", cela ne semble donc pas être le cas ici.

16
Aaron Bertrand
  1. Le champ de votre condition où l'état n'est pas le champ de premier plan de l'index.

  2. Vous avez measure défini comme nvarchar alors préfixe le littéral avec un N: where Measure = N'FinanceFICOScore'.

Envisagez de créer un indice en cluster sur SnapshotKey. S'il est unique, cela peut être un PK (et regroupé). Si ce n'est pas unique, il ne peut pas être un PK, mais peut toujours être un index en cluster non unique. Ensuite, votre index non en cluster ne serait que sur la colonne measure.

Et, compte tenu du fait que le premier champ de l'GROUP BY est aussi measure, qui bénéficierait également d'avoir measure être le champ de premier plan.

En fait, pour cette opération, vous devrez peut-être plutôt définir l'index non clustered sur Measure, SnapshotKey, MeasureBand, dans cet ordre exact car il correspond à la GROUP BY clause. Taille-sage qui n'ajoute vraiment que MeasureBand puisque l'index non clustered est déjà basé sur Measure et MeasureKey est déjà inclus dans l'index car il est maintenant la clé d'index en clusterie. (Non, Measure ne sera pas dupliqué dans l'indice non clustered).

@ROB avait mentionné dans un commentaire maintenant supprimé sur sa réponse que la résolution de cette question nécessite uniquement que l'indice non clustered soit défini avec ces trois champs dans cet ordre et que la création d'un index clustered (non unique) sur SnapshotKey n'est pas nécessaire. Bien qu'il soit probablement correct (j'espérais que moins de champs travailleraient), je voudrais toujours que l'indice en cluster est bénéfique pour non seulement cette opération, mais probablement la plupart des autres.

6
Solomon Rutzky