Какие факторы влияют на выбор кластеризованного индекса в индексированном представлении?

19

Вкратце
Какие факторы влияют на выбор оптимизатором индекса индексированного представления?

Мне кажется, что индексированные представления не поддаются пониманию того, как оптимизатор выбирает индексы. Я уже видел этот вопрос раньше , но ОП был не слишком хорошо принят. Я действительно ищу ориентиры , но я придумаю псевдо-пример, а затем опубликую реальный пример с большим количеством DDL, выводом, примерами.

Предположим, я использую Enterprise 2008+, понимаю with(noexpand)

Пример псевдо

Возьмите этот псевдо-пример: я создаю представление с 22 объединениями, 17 фильтрами и цирковым пони, который пересекает группу из 10 миллионов строк. Это представление дорого (да, с большой буквы E), чтобы материализоваться. Я SCHEMABIND и индексировать представление. Тогда а SELECT a,b FROM AnIndexedView WHERE theClusterKeyField < 84. В логике оптимизатора, которая ускользает от меня, выполняются базовые объединения.

Результат:

  • Нет подсказки: 4825 считываний для 720 строк, 47 процессоров в течение 76 мс и предполагаемая стоимость поддерева 0,30523.
  • С подсказкой: 17 операций чтения, 720 строк, 15 процессоров в течение 4 мс и приблизительная стоимость поддерева 0,007253

Так что здесь происходит? Я пробовал это в Enterprise 2008, 2008-R2 и 2012. По всем показателям, которые я могу использовать, использование индекса представления значительно эффективнее. У меня нет проблем с прослушиванием параметров или перекосом данных, так как это рекламный хоккей.

Реальный (длинный) пример

Если вы не мазохист, вам, вероятно, не нужно или вы не хотите читать эту часть.

Версия
Да, предприятие.

Microsoft SQL Server 2012 - 11.0.2100.60 (X64) 10 февраля 2012 г. 19:39:15 Авторское право (c) Microsoft Corporation Enterprise Edition (64-разрядная версия) в Windows NT 6.2 (сборка 9200:) (гипервизор)

Вид

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

Кластерный индекс

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

Тест 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'

Результат = 11 строк вывода

11 строк вывода - одинаково для обоих запросов

Профилировщик Вывод
Верхние 4 строки без подсказки. Нижние 4 строки используют подсказку.

Profiler

Планы выполнения
GitHub Gist для обоих планов выполнения в формате SQLPlan

Нет Подсказка План выполнения - почему бы не использовать кластерный индекс, который я дал вам мистер SQL? Он сгруппирован по 3 полям фильтра. Попробуйте, вам это может понравиться.
Нет подсказки - огромный план исполнения

Простой план при использовании подсказки.

Использование подсказки - простой план выполнения

EBarr
источник
Обсуждение перешло в чат
Пол Уайт говорит, что GoFundMonica

Ответы:

26

Сопоставление индексированных представлений является относительно дорогой операцией *, поэтому оптимизатор сначала пробует другие быстрые и простые преобразования. Если из-за этого получается дешевый план (в вашем случае 0,05 единицы), оптимизация заканчивается рано. Ставка на то, что продолжение оптимизации потребует больше времени, чем сэкономлено. Помните, что основной целью оптимизатора является «достаточно хороший» план быстро.

Использование кластеризованного индекса в представлении само по себе не дорого, но процесс сопоставления логического дерева запросов с потенциальными индексированными представлениями может быть дорогим. Как я уже упоминал в комментарии к другому вопросу, ссылка на представление в запросе расширяется до оптимизации, поэтому оптимизатор не знает, что вы написали запрос к представлению, в первую очередь - он видит только расширенное дерево (как будто вид был в подкладке).

«Достаточно хороший план» означает, что оптимизатор нашел достойный план и остановился на ранней стадии исследования. «TimeOut» означает, что оно превысило количество шагов оптимизации, которые оно установило как «бюджет» в начале текущей фазы.

Бюджет устанавливается на основе стоимости лучшего плана, найденного на предыдущем этапе. При таком недорогом запросе (0,05) количество перемещений в бюджете будет довольно небольшим и будет быстро исчерпано регулярным преобразованием, учитывая количество объединений, включенных в ваш пример запроса (например, существует множество способов перегруппировать внутренние объединения) ,

Если вам интересно узнать больше о том, почему сопоставление индексированных представлений стоит дорого и поэтому оставлено для более поздних этапов оптимизации и / или рассматривается только для более дорогостоящих запросов, есть две статьи Microsoft Research по этой теме здесь (pdf) и здесь (citeseer ).

Другим важным фактором является то, что индексированное сопоставление представлений недоступно на этапе оптимизации 0 (обработка транзакций).

Дальнейшее чтение:

Индексированные представления и статистика

* и доступно только в версии Enterprise Edition (или эквивалентной)

Пол Уайт говорит, что GoFundMonica
источник