Sqlserver
 sql >> Base de données >  >> RDS >> Sqlserver

Pourquoi Query Optimizer ignore totalement les index de vue indexés ?

tl;dr réponse :Si vous ne spécifiez pas NOEXPAND, l'optimiseur de requête n'a aucune idée que vous soumettez une simple sélection à partir d'une vue. Il devrait correspondre à l'expansion de votre requête (qui est tout ce qu'elle voit) avec un index de vue. Cela ne vous dérangera probablement pas quand il s'agira d'une jointure à cinq avec un tas de distributions.

Voir l'index correspondant à une requête est un problème difficile, et je pense que votre vue est trop compliquée pour que le moteur de requête corresponde à un index. Considérez l'une de vos requêtes :

SELECT ID, Code1 FROM EntityView Where Code1 > 'NR%';

Il est évident pour vous que cela peut utiliser un index de vue, mais ce n'est pas la requête que le moteur de requête voit. Les vues sont automatiquement développées si vous ne spécifiez pas NOEXPAND, c'est donc ce qui va au moteur de requête :

SELECT ID, Code1 FROM (
    SELECT e.ID, 'NR'+CAST(c1.CODE as nvarchar(11)) as Code1, 'NR'+CAST(c2.CODE as nvarchar(11)) as Code2, 'NR'+CAST(c3.CODE as nvarchar(11)) as Code3, 'NR'+CAST(c4.CODE as nvarchar(11)) as Code4, 'NR'+CAST(c5.CODE as nvarchar(11)) as Code5
    FROM dbo.Entity e
        inner join  dbo.Classificator1 c1 on e.ID = c1.ID
        inner join  dbo.Classificator2 c2 on e.ID = c2.ID
        inner join  dbo.Classificator3 c3 on e.ID = c3.ID
        inner join  dbo.Classificator4 c4 on e.ID = c4.ID
        inner join  dbo.Classificator5 c5 on e.ID = c5.ID;
) AS V;

Le moteur de requête voit cette requête compliquée, et il a des informations (mais probablement pas le SQL des définitions de vue) qui décrivent les index de vue qui ont été définis. Étant donné que cette requête et les index de vue ont tous deux plusieurs jointures et transtypages, la correspondance est un travail difficile.

Gardez à l'esprit que vous savez que les jointures et les correspondances sont identiques dans cette requête et dans les index de vue, mais que le processeur de requêtes ne le sait pas. Il traite cette requête de la même manière que si elle rejoignait cinq copies de Classificator3, ou si l'une des colonnes était 'NQ'+CAST(c2.CODE as varchar(12)). Le comparateur d'index de vue (en supposant qu'il ait tenté de faire correspondre cette requête compliquée) devrait faire correspondre chaque détail de cette requête aux détails des index de vue sur les tables concernées.

Le moteur de requête a pour objectif n°1 de trouver un moyen d'exécuter efficacement la requête. Il n'est probablement pas conçu pour passer beaucoup de temps à essayer de faire correspondre chaque détail d'une jointure à cinq voies et de CAST à un index de vue.

Si je devais deviner, je soupçonne que le comparateur d'index de vue voit que les colonnes de résultat de la requête ne sont même pas des colonnes d'une table sous-jacente (à cause du CAST) et ne prend tout simplement pas la peine d'essayer quoi que ce soit. Ajouté :Je me trompe. Je viens d'essayer la suggestion de Martin de mettre à jour les statistiques pour rendre la requête coûteuse, et un index de vue a été mis en correspondance pour certaines de ces requêtes sans NOEXPAND. Le matcher de vue est plus intelligent que je ne le pensais! Le problème est donc que le comparateur de vue essaie probablement plus de faire correspondre une requête compliquée si son coût est très élevé.

Utilisez l'indicateur NOEXPAND au lieu de vous attendre à ce que le moteur de requête puisse déterminer ce qui correspond ici. NOEXPAND est absolument votre ami, car alors le moteur de requête peut voir

SELECT ID, Code1 FROM EntityView Where Code1 > 'NR%';

et il est alors immédiatement évident pour le comparateur d'index de vue qu'il existe un index utile.

(Remarque :Votre code SQL Fiddle contient les 5 références de clé étrangère à la même table, ce qui n'est probablement pas ce que vous souhaitez.)