Quels facteurs entrent en jeu dans la sélection d'un index clusterisé d'une vue indexée?

19

En bref
Quels facteurs entrent en jeu lorsqu'ils interrogent l'optimiseur sur la sélection de l'index d'une vue indexée?

Pour moi, les vues indexées semblent défier ce que je comprends de la façon dont l'Optimizer sélectionne les index. J'ai déjà vu cette question , mais le PO n'a pas été bien reçu. Je suis vraiment à la recherche de repères , mais je vais concocter un pseudo exemple, puis poster un exemple réel avec beaucoup de DDL, des sorties, des exemples.

Supposons que j'utilise Enterprise 2008+, comprenez with(noexpand)

Pseudo-exemple

Prenons ce pseudo-exemple: je crée une vue avec 22 jointures, 17 filtres et un poney de cirque qui traverse un tas de 10 millions de tables de lignes. Cette vue est coûteuse (oui, avec un E majuscule) pour se matérialiser. Je vais SCHEMABIND et indexer la vue. Alors a SELECT a,b FROM AnIndexedView WHERE theClusterKeyField < 84. Dans la logique Optimizer qui m'échappe, les jointures sous-jacentes sont effectuées.

Le résultat:

  • Pas d'indice: 4825 lectures pour 720 lignes, 47 processeurs sur 76 ms et un coût estimé de sous-arbre de 0,30523.
  • Avec indice: 17 lectures, 720 lignes, 15 processeurs sur 4 ms et un coût estimé des sous-arbres de 0,007253

Alors qu'est-ce qui se passe ici? Je l'ai essayé dans Enterprise 2008, 2008-R2 et 2012. Par chaque métrique, je peux penser que l'utilisation de l'index de la vue est beaucoup plus efficace. Je n'ai pas de problème de reniflage de paramètres ou de données biaisées, car il s'agit de jarrets publicitaires.

Un vrai (long) exemple

Sauf si vous êtes un peu masochiste, vous n'avez probablement pas besoin ou ne voulez pas lire cette partie.

La version
Yep, entreprise.

Microsoft SQL Server 2012 - 11.0.2100.60 (X64) 10 février 2012 19:39:15 Copyright (c) Microsoft Corporation Enterprise Edition (64 bits) sur Windows NT 6.2 (Build 9200:) (Hyperviseur)

La vue

CREATE VIEW dbo.TimelineMaterialized    WITH SCHEMABINDING
AS
SELECT  TM.TimelineID,
        TM.TimelineTypeID,
        TM.EmployeeID,
        TM.CreateUTC,
        CUL.CultureCode,
        CASE 
           WHEN TM.CustomerMessageID    > 0 THEN TM.CustomerMessageID
           WHEN TM.CustomerSessionID    > 0 THEN TM.CustomerSessionID
           WHEN TM.NewItemTagID         > 0 THEN TM.NewItemTagID
           WHEN TM.OutfitID             > 0 THEN TM.OutfitID
           WHEN TM.ProductTransactionID > 0 THEN TM.ProductTransactionID
           ELSE 0 END  As HrefId,
        CASE 
          WHEN TM.CustomerMessageID    > 0 THEN IsNull(C.Name, 'N/A')   
          WHEN TM.CustomerSessionID    > 0 THEN IsNull(C.Name, 'N/A')
          WHEN TM.NewItemTagID         > 0 THEN IsNull(NI.Title, 'N/A')
          WHEN TM.OutfitID             > 0 THEN IsNull(O.Name, 'N/A')
          WHEN TM.ProductTransactionID > 0 THEN IsNull(PT_PL.NameLocalized, 'N/A')
                 END as HrefText

FROM       dbo.Timeline TM
INNER JOIN dbo.CustomerSession    CS    ON TM.CustomerSessionID    = CS.CustomerSessionID
INNER JOIN dbo.CustomerMessage    CM    ON TM.CustomerMessageID    = CM.CustomerMessageID
INNER JOIN dbo.Outfit             O     ON PO.OutfitID             = O.OutfitID
INNER JOIN dbo.ProductTransaction PT    ON TM.ProductTransactionID = PT.ProductTransactionID
INNER JOIN dbo.Product            PT_P  ON PT.ProductID            = PT_P.ProductID
INNER JOIN dbo.ProductLang        PT_PL ON PT_P.ProductID          = PT_PL.ProductID
INNER JOIN dbo.Culture            CUL   ON PT_PL.CultureID         = CUL.CultureID
INNER JOIN dbo.NewsItemTag        NIT   ON TM.NewsItemTagID        = NIT.NewsItemTagID
INNER JOIN dbo.NewsItem           NI    ON NIT.NewsItemID          = NI.NewsItemID
INNER JOIN dbo.Customer           C     ON  C.CustomerID = CASE 
                                             WHEN TM.TimelineTypeID = 1 THEN CM.CustomerID 
                                             WHEN TM.TimelineTypeID = 5 THEN CS.CustomerID
                                             ELSE 0 END

WHERE        CUL.IsActive = 1

Index clusterisé

CREATE UNIQUE CLUSTERED INDEX PK_TimelineMaterialized  ON 
                   TimelineMaterialized (EmployeeID, CreateUTC, CultureCode, TimelineID)

Tester SQL

-- NO HINT - - -  - - -  - - -  - - -  - - - 
SELECT  *                 --yes yes, star is bad ...just a test example
FROM    TimelineMaterialized TM 
WHERE 
            TM.EmployeeID   = 2
        AND TM.CultureCode  = 'en-US'
        AND TM.CreateUTC    > '9/10/2012'
        AND TM.CreateUTC    < '9/11/2012'

-- WITH HINT - - -  - - -  - - -  - - -  - - - 
SELECT  *               
FROM    TimelineMaterialized TM with(noexpand)
WHERE 
            TM.EmployeeID   = 2
        AND TM.CultureCode  = 'en-US'
        AND TM.CreateUTC    > '9/10/2012'
        AND TM.CreateUTC    < '9/11/2012'

Résultat = 11 lignes de sortie

11 lignes de sortie - identiques pour les deux requêtes

Sortie du profileur
Les 4 premières lignes sont sans indice. Les 4 dernières lignes utilisent l'indice.

Profiler

Plans d'exécution
GitHub Gist pour les deux plans d'exécution au format SQLPlan

No Hint Execution plan - pourquoi ne pas utiliser l'index cluster que je vous ai donné Mr. SQL? C'est clusterd sur les 3 champs de filtre. Essayez-le, vous pourriez aimer.
No Hint - énorme plan d'exécution

Plan simple lors de l'utilisation d'un indice.

Utilisation de Hint - Plan d'exécution simple

EBarr
la source
Discussion déplacée vers le chat
Paul White dit GoFundMonica

Réponses:

26

La correspondance des vues indexées est une opération relativement coûteuse *, l'optimiseur essaie donc d'abord d'autres transformations rapides et faciles. Si ceux-ci produisent un plan bon marché (0,05 unités dans votre cas), l'optimisation se termine tôt. Le pari est que l'optimisation continue consommerait plus de temps qu'elle n'en a économisé. N'oubliez pas que l'objectif principal de l'optimiseur est un plan «assez bon» rapidement.

L'utilisation de l'index cluster sur la vue n'est pas coûteuse en soi, mais le processus de mise en correspondance d'un arbre de requête logique avec des vues indexées potentielles peut l'être. Comme je l'ai mentionné dans un commentaire sur l'autre question, la référence de vue dans la requête est développée avant l'optimisation, de sorte que l'optimiseur ne sait pas que vous avez écrit la requête sur la vue en premier lieu - il ne voit que l'arborescence développée (comme si la vue avait été alignée).

«Bon plan suffisant» signifie que l'optimiseur a trouvé un plan décent et s'est arrêté tôt dans une phase d'exploration. «TimeOut» signifie qu'il a dépassé le nombre d'étapes d'optimisation qu'il s'est fixé comme «budget» au début de la phase en cours.

Le budget est établi en fonction du coût du meilleur plan trouvé dans une phase précédente. Avec une requête aussi peu coûteuse (0,05), le nombre de mouvements budgétés sera assez petit et rapidement épuisé par une transformation régulière étant donné le nombre de jointures impliquées dans votre exemple de requête (il existe de nombreuses façons de réorganiser les jointures internes, par exemple) .

Si vous souhaitez en savoir plus sur les raisons pour lesquelles la correspondance des vues indexées est coûteuse, et donc réservée aux étapes ultérieures d'optimisation et / ou uniquement envisagée pour les requêtes plus coûteuses, il existe deux documents de recherche Microsoft sur le sujet ici (pdf) et ici (citeseer ).

Un autre facteur pertinent est que la correspondance des vues indexées n'est pas disponible dans la phase d'optimisation 0 (traitement des transactions).

Lectures complémentaires:

Vues et statistiques indexées

* et uniquement disponible dans Enterprise Edition (ou équivalent)

Paul White dit GoFundMonica
la source