План выполнения показывает дорогую операцию CONVERT_IMPLICIT. Можно ли исправить это с помощью индексации или мне нужно изменить таблицу?

24

У меня очень важное, очень медленное представление, которое включает в себя некоторые действительно уродливые условия, подобные этому, в предложении where. Мне также известно, что объединения - это грубые и медленные объединения varchar(13)вместо целочисленных идентификаторов, но я хотел бы улучшить простой запрос ниже, который использует это представление:

CREATE VIEW [dbo].[vwReallySlowView]  AS  
AS  
SELECT     
  I.booking_no_v32 AS bkno, 
  I.trans_type_v41 AS trantype, 
  B.Assigned_to_v61 AS Assignbk, 
  B.order_date AS dateo, B.HourBooked AS HBooked,   
  B.MinBooked AS MBooked, B.SecBooked AS SBooked, 
  I.prep_on AS Pon, I.From_locn AS Flocn, 
  I.Trans_to_locn AS TTlocn,   
                      (CASE I.prep_on WHEN 'Y' THEN I.PDate ELSE I.FirstDate END) AS PrDate, I.PTimeH AS PrTimeH, I.PTimeM AS PrTimeM,   
                      (CASE WHEN I.RetnDate < I.FirstDate THEN I.FirstDate ELSE I.RetnDate END) AS RDatev, I.bit_field_v41 AS bitField, I.FirstDate AS FDatev, I.BookDate AS DBooked,   
                      I.TimeBookedH AS TBookH, I.TimeBookedM AS TBookM, I.TimeBookedS AS TBookS, I.del_time_hour AS dth, I.del_time_min AS dtm, I.return_to_locn AS rtlocn,   
                      I.return_time_hour AS rth, I.return_time_min AS rtm, (CASE WHEN I.Trans_type_v41 IN (6, 7) AND (I.Trans_qty < I.QtyCheckedOut)   
                      THEN 0 WHEN I.Trans_type_v41 IN (6, 7) AND (I.Trans_qty >= I.QtyCheckedOut) THEN I.Trans_Qty - I.QtyCheckedOut ELSE I.trans_qty END) AS trqty,   
                      (CASE WHEN I.Trans_type_v41 IN (6, 7) THEN 0 ELSE I.QtyCheckedOut END) AS MyQtycheckedout, (CASE WHEN I.Trans_type_v41 IN (6, 7)   
                      THEN 0 ELSE I.QtyReturned END) AS retqty, I.ID, B.BookingProgressStatus AS bkProg, I.product_code_v42, I.return_to_locn, I.AssignTo, I.AssignType,   
                      I.QtyReserved, B.DeprepOn,  
        (CASE  B.DeprepOn       
        WHEN 1 THEN  B.DeprepDateTime     
        ELSE   I.RetnDate  
           END)  AS DeprepDateTime, I.InRack 
FROM         dbo.tblItemtran AS I 

INNER JOIN  -- booking_no = varchar(13)
         dbo.tblbookings AS B ON B.booking_no = I.booking_no_v32  --  string inner-join

INNER JOIN  -- product_code = varchar(13) 
        dbo.tblInvmas AS M ON I.product_code_v42 = M.product_code  --  string inner-join

WHERE     (I.trans_type_v41 NOT IN (2, 3, 7, 18, 19, 20, 21, 12, 13, 22)) AND (I.trans_type_v41 NOT IN (6, 7)) AND (I.bit_field_v41 & 4 = 0) OR  
                      (I.trans_type_v41 NOT IN (6, 7)) AND (I.bit_field_v41 & 4 = 0) AND (B.BookingProgressStatus = 1) OR  
                      (I.trans_type_v41 IN (6, 7)) AND (I.bit_field_v41 & 4 = 0) AND (I.QtyCheckedOut = 0) OR  
                      (I.trans_type_v41 IN (6, 7)) AND (I.bit_field_v41 & 4 = 0) AND (I.QtyCheckedOut > 0) AND (I.trans_qty - (I.QtyCheckedOut - I.QtyReturned) > 0)  

Это представление обычно используется так:

select * from vwReallySlowView
where product_code_v42  = 'LIGHTBULB100W'  -- find "100 watt lightbulb" rows

Когда я запускаю его, я получаю этот элемент плана выполнения стоимостью от 20 до 80% от общей стоимости пакета с предикатом, CONVERT_IMPLICIT( .... &(4))показывающим, что он, похоже, очень медленно выполняет bitwise boolean testsподобные действия (I.ibitfield & 4 = 0).

Я не являюсь экспертом по MS SQL или по типу работы администратора баз данных, так как большую часть времени я не являюсь разработчиком программного обеспечения, отличного от SQL. Но я подозреваю, что такие побитовые комбинации - плохая идея, и что было бы лучше иметь дискретные логические поля.

Могу ли я каким-то образом улучшить этот индекс, чтобы лучше обрабатывать это представление, не меняя схему (которая уже используется в тысячах местоположений), или я должен изменить базовую таблицу, в которой несколько логических значений упакованы в целое число bit_field_v41, чтобы решить эту проблему ?

Вот мой кластерный индекс, tblItemtranкоторый сканируется в этом плане выполнения:

-- goal:  speed up  select * from vwReallySlowView where productcode  = 'X'
CREATE CLUSTERED INDEX [idxtblItemTranProductCodeAndTransType] ON [dbo].[tblItemtran] 
(
    [product_code_v42] ASC,  -- varchar(13)
    [trans_type_v41] ASC     -- int
)WITH ( PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, 
        IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, 
        ALLOW_PAGE_LOCKS  = ON) 
ON [PRIMARY]

Вот план выполнения для одного из других продуктов, который приводит к 27% -ной стоимости для этого CONVERT_IMPLICITпредиката. Обновление Обратите внимание, что в этом случае мой худший узел теперь "соответствует хешу" для an inner join, что стоит 34%. Я считаю, что это цена, которую я не могу избежать, если не смогу избежать объединения строк, которые в настоящее время я не могу избавиться от. Обе INNER JOINоперации в представлении выше относятся к varchar(13)полям.

Увеличено в правом нижнем углу:

введите описание изображения здесь

Весь план выполнения в виде .sqlplan доступен на skydrive. Это изображение просто визуальный обзор. Нажмите здесь, чтобы увидеть изображение само по себе.

введите описание изображения здесь

Обновление опубликовано весь план выполнения. Кажется, я не могу понять, какая product_codeценность была патологически плохой, но один из способов сделать это - select count(*) from viewвместо одного продукта. Но продукты, которые используются только в 5% записей в базовой таблице или менее, по-видимому, показывают гораздо более низкие затраты на CONVERT_IMPLICIT операцию. Если бы я собирался исправить SQL здесь, я думаю, что я бы взял грубое WHEREпредложение в представлении, вычислил и сохранил результат этого гигантского условия where-условия в виде битового поля «IncludeMeInTheView» в базовой таблице. , Presto, проблема решена, верно?

Уоррен П
источник
Опубликованный мною .SQLPLAN гораздо менее патологичен, чем случай 432k / 17k в исходном изображении. Я извиняюсь, я больше не могу найти product_codeзначение, которое я использовал для генерации этого патологического 87%случая с моими данными. Изображения теперь показывают 27%. Опять же, извинения за путаницу из-за моих правок.
Уоррен П

Ответы:

49

Вы не должны слишком полагаться на процентные доли затрат в планах выполнения. Это всегда предполагаемые затраты , даже в планах после выполнения с «фактическими» числами для таких вещей, как количество строк. Сметные расходы основаны на модели, которая работает достаточно хорошо для той цели, для которой она предназначена: дать оптимизатору возможность выбирать между различными подходящими планами выполнения для одного и того же запроса. Информация о затратах интересна и является важным фактором, но она редко должна быть основным показателем для настройки запросов. Интерпретация информации плана выполнения требует более широкого представления представленных данных.

Оператор поиска кластерного индекса ItemTran

Поиск кластерного индекса ItemTran

Этот оператор действительно две операции в одной. Сначала операция поиска по индексу находит все строки, соответствующие предикату product_code_v42 = 'M10BOLT', затем к каждой строке применяется остаточный предикат bit_field_v41 & 4 = 0. Существует неявное преобразование bit_field_v41из его базового типа ( tinyintили smallint) в integer.

Преобразование происходит потому, что оператор побитового И (&) требует, чтобы оба операнда были одного типа. Неявный тип константного значения '4' является целым числом, а правила приоритета типа данных означают, что значение bit_field_v41поля с более низким приоритетом преобразуется.

Проблема (такая, как она есть) легко может быть исправлена ​​записью предиката как, то bit_field_v41 & CONVERT(tinyint, 4) = 0есть постоянное значение имеет более низкий приоритет и преобразуется (во время свертывания константы), а не в значение столбца. Если bit_field_v41это tinyintпреобразование не происходит вообще. Аналогично, CONVERT(smallint, 4)может быть использовано, если bit_field_v41есть smallint. Тем не менее, преобразование не является проблемой производительности в этом случае, но все же рекомендуется использовать сопоставление типов и избегать неявных преобразований, где это возможно.

Основная часть сметных затрат на поиск сводится к размеру базовой таблицы. Хотя ключ кластеризованного индекса сам по себе достаточно узок, размер каждой строки велик. Определение для таблицы не дано, но только столбцы, используемые в представлении, добавляют значительную ширину строки. Поскольку кластерный индекс включает в себя все столбцы, расстояние между ключами кластерного индекса является шириной строки , а не шириной ключей индекса . Использование суффиксов версий в некоторых столбцах предполагает, что в реальной таблице еще больше столбцов для предыдущих версий.

Если посмотреть на столбцы поиска, остаточного предиката и вывода, то производительность этого оператора можно проверить изолированно, создав эквивалентный запрос ( 1 <> 2это хитрость, предотвращающая автопараметризацию, противоречие устраняется оптимизатором и не отображается в план запроса):

SELECT
    it.booking_no_v32,
    it.QtyCheckedOut,
    it.QtyReturned,
    it.Trans_qty,
    it.trans_type_v41
FROM dbo.tblItemTran AS it
WHERE
    1 <> 2
    AND it.product_code_v42 = 'M10BOLT'
    AND it.bit_field_v41 & CONVERT(tinyint, 4) = 0;

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

Я выполнил тест влияния фрагментации на упреждающее чтение, используя образцы данных, сгенерированные с помощью генератора данных SQL . При использовании того же количества строк в таблице, как показано в плане запроса вопроса, кластеризованный индекс с высокой степенью фрагментации привел к тому, SELECT * FROM viewчто после этого потребовалось 15 секунд DBCC DROPCLEANBUFFERS. Тот же тест в тех же условиях со вновь перестроенным кластерным индексом в таблице ItemTrans завершился за 3 секунды.

Если данные таблицы, как правило, полностью находятся в кеше, проблема фрагментации будет гораздо менее важной. Но даже при низкой фрагментации широкие строки таблицы могут означать, что число логических и физических чтений намного выше, чем можно было ожидать. Вы также можете поэкспериментировать с добавлением и удалением явного, CONVERTчтобы подтвердить мое ожидание, что проблема неявного преобразования здесь не важна, за исключением случаев нарушения правил.

Более конкретно, это приблизительное количество строк, оставленных оператором поиска. Оценка времени оптимизации составляет 165 строк, но 4226 были получены во время выполнения. Я вернусь к этому вопросу позже, но главная причина расхождений заключается в том, что оптимизатору остаточный предикат (включающий побитовое И) очень сложно предсказать - фактически он прибегает к угадыванию.

Оператор фильтра

Оператор фильтра

Здесь я показываю предикат фильтра в основном, чтобы проиллюстрировать, как эти два NOT INсписка объединяются, упрощаются, а затем расширяются, а также чтобы дать ссылку для последующего обсуждения совпадения хеша. Тестовый запрос от поиска может быть расширен, чтобы включить его эффекты и определить влияние оператора Filter на производительность:

SELECT
    it.booking_no_v32,
    it.trans_type_v41,
    it.Trans_qty,
    it.QtyReturned,
    it.QtyCheckedOut
FROM dbo.tblItemTran AS it
WHERE
    it.product_code_v42 = 'M10BOLT'
    AND it.bit_field_v41 & CONVERT(tinyint, 4) = 0
    AND
    (
        (
            it.trans_type_v41 NOT IN (2, 3, 6, 7, 18, 19, 20, 21, 12, 13, 22)
            AND it.trans_type_v41 NOT IN (6, 7)
        )
        OR
        (
            it.trans_type_v41 NOT IN (6, 7)
        )
        OR 
        (
            it.trans_type_v41 IN (6, 7)
            AND it.QtyCheckedOut = 0
        )
        OR 
        (
            it.trans_type_v41 IN (6, 7)
            AND it.QtyCheckedOut > 0
            AND it.trans_qty - (it.QtyCheckedOut - it.QtyReturned) > 0
        )
    );

Оператор Compute Scalar в плане определяет следующее выражение (само вычисление откладывается до тех пор, пока результат не потребуется последующему оператору):

[Expr1016] = (trans_qty - (QtyCheckedOut - QtyReturned))

Оператор хэш-матча

Выполнение объединения для символьных типов данных не является причиной высокой оценочной стоимости этого оператора. Во всплывающей подсказке SSMS отображается только запись «Зонд хэш-ключей», но важные детали находятся в окне свойств SSMS.

Оператор Hash Match создает хеш-таблицу, используя значения booking_no_v32столбца (Hash Keys Build) из таблицы ItemTran, а затем проверяет совпадения, используя booking_noстолбец (Hash Keys Probe) из таблицы Bookings. Во всплывающей подсказке SSMS также обычно отображается «Остаток зонда», но текст слишком длинный для всплывающей подсказки и просто пропускается.

Остаток зондирования аналогичен остатку, замеченному после поиска индекса ранее; остаточный предикат оценивается во всех строках, которые соответствуют хешу, чтобы определить, должна ли строка быть передана родительскому оператору. Поиск совпадений хеша в хорошо сбалансированной хеш-таблице чрезвычайно быстр, но применение сложного остаточного предиката к каждой строке, которая соответствует, сравнительно медленное по сравнению. Во всплывающей подсказке «Hash Match» в Plan Explorer отображаются подробные сведения, включая выражение остаточного зонда:

Оператор хэш-матча

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

Недооценка количества строк является проблемой для оператора Hash Match, поскольку объем памяти, зарезервированный для хеш-таблицы, основан на приблизительном количестве строк. Если объем памяти слишком мал для размера хеш-таблицы, требуемой во время выполнения (из-за большего числа строк), хеш-таблица рекурсивно перетекает в физическое хранилище tempdb , что часто приводит к очень низкой производительности. В худшем случае механизм выполнения останавливает рекурсивное разливание хэш-блоков и использует очень медленныйалгоритм спасения. Распределение хэша (рекурсивное или спасение) является наиболее вероятной причиной проблем с производительностью, описанных в вопросе (не в столбцах соединения символьного типа или неявных преобразованиях). Основной причиной может быть то, что сервер зарезервировал слишком мало памяти для запроса на основании неверного подсчета количества строк (количества элементов).

К сожалению, до SQL Server 2012 в плане выполнения не было указаний на то, что операция хеширования превысила свое выделение памяти (которая не может динамически увеличиваться после резервирования до начала выполнения, даже если на сервере есть много свободной памяти) и должна была перетекать в Tempdb. Можно отслеживать класс событий хэш-предупреждений с помощью Profiler, но может быть сложно соотнести предупреждения с конкретным запросом.

Исправлять проблемы

Три проблемы - это фрагментация, сложный остаток зонда в операторе совпадения хеша и неправильная оценка количества элементов, полученная в результате угадывания при поиске по индексу.

Рекомендуемое решение

Проверьте фрагментацию и исправьте ее при необходимости, составив график обслуживания, чтобы убедиться, что индекс остается приемлемо организованным. Обычный способ исправить оценку количества элементов - предоставить статистику. В этом случае оптимизатору нужна статистика для комбинации ( product_code_v42, bitfield_v41 & 4 = 0). Мы не можем создавать статистику по выражению напрямую, поэтому мы должны сначала создать вычисляемый столбец для выражения битового поля, а затем создать статистику по нескольким столбцам вручную:

ALTER TABLE dbo.tblItemTran
ADD Bit3 AS bit_field_v41 & CONVERT(tinyint, 4);

CREATE STATISTICS [stats dbo.ItemTran (product_code_v42, Bit3)]
ON dbo.tblItemTran (product_code_v42, Bit3);

Текстовое определение вычисляемого столбца должно в точности соответствовать тексту в определении представления для используемой статистики, поэтому исправление представления для устранения неявного преобразования должно быть выполнено одновременно, и необходимо обеспечить соответствие текста.

Многостолбцовая статистика должна приводить к гораздо лучшим оценкам, значительно снижая вероятность того, что оператор совпадения хэшей будет использовать рекурсивный сброс или алгоритм спасения. Добавление вычисляемого столбца (который является операцией только для метаданных и не занимает места в таблице, поскольку она не помечена PERSISTED) и статистика по нескольким столбцам - мое лучшее предположение при первом решении.

При решении проблем с производительностью запросов важно измерять такие вещи, как истекшее время, загрузка ЦП, логическое чтение, физическое чтение, типы ожидания и продолжительность ... и так далее. Также может быть полезно запустить части запроса отдельно для проверки предполагаемых причин, как показано выше.

В некоторых средах, где представление данных с точностью до секунды не важно, может быть полезно запускать фоновый процесс, который материализует все представление в таблицу снимков время от времени. Эта таблица является обычной базовой таблицей и может быть проиндексирована для запросов на чтение, не беспокоясь о влиянии на производительность обновления.

Просмотр индексации

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

Расширенное решение с частичным индексированным представлением

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

-- Indexed view to optimize the main view
CREATE VIEW dbo.V1
WITH SCHEMABINDING
AS
SELECT
    it.ID,
    it.product_code_v42,
    it.trans_type_v41,
    it.booking_no_v32,
    it.Trans_qty,
    it.QtyReturned,
    it.QtyCheckedOut,
    it.QtyReserved,
    it.bit_field_v41,
    it.prep_on,
    it.From_locn,
    it.Trans_to_locn,
    it.PDate,
    it.FirstDate,
    it.PTimeH,
    it.PTimeM,
    it.RetnDate,
    it.BookDate,
    it.TimeBookedH,
    it.TimeBookedM,
    it.TimeBookedS,
    it.del_time_hour,
    it.del_time_min,
    it.return_to_locn,
    it.return_time_hour,
    it.return_time_min,
    it.AssignTo,
    it.AssignType,
    it.InRack
FROM dbo.tblItemTran AS it
JOIN dbo.tblBookings AS tb ON
    tb.booking_no = it.booking_no_v32
WHERE
    (
        it.trans_type_v41 NOT IN (2, 3, 7, 18, 19, 20, 21, 12, 13, 22)
        AND it.trans_type_v41 NOT IN (6, 7)
        AND it.bit_field_v41 & CONVERT(tinyint, 4) = 0
    )
    OR
    (
        it.trans_type_v41 NOT IN (6, 7)
        AND it.bit_field_v41 & CONVERT(tinyint, 4) = 0
        AND tb.BookingProgressStatus = 1
    )
    OR 
    (
        it.trans_type_v41 IN (6, 7)
        AND it.bit_field_v41 & CONVERT(tinyint, 4) = 0
        AND it.QtyCheckedOut = 0
    )
    OR 
    (
        it.trans_type_v41 IN (6, 7)
        AND it.bit_field_v41 & CONVERT(tinyint, 4) = 0
        AND it.QtyCheckedOut > 0
        AND it.trans_qty - (it.QtyCheckedOut - it.QtyReturned) > 0
    );
GO
CREATE UNIQUE CLUSTERED INDEX cuq ON dbo.V1 (product_code_v42, ID);
GO

Существующее представление настроено для использования индексированного представления выше:

CREATE VIEW [dbo].[vwReallySlowView2]
AS
SELECT
    I.booking_no_v32 AS bkno,
    I.trans_type_v41 AS trantype,
    B.Assigned_to_v61 AS Assignbk,
    B.order_date AS dateo,
    B.HourBooked AS HBooked,
    B.MinBooked AS MBooked,
    B.SecBooked AS SBooked,
    I.prep_on AS Pon,
    I.From_locn AS Flocn,
    I.Trans_to_locn AS TTlocn,
    CASE I.prep_on 
        WHEN 'Y' THEN I.PDate
        ELSE I.FirstDate
    END AS PrDate,
    I.PTimeH AS PrTimeH,
    I.PTimeM AS PrTimeM,
    CASE
        WHEN I.RetnDate < I.FirstDate 
        THEN I.FirstDate 
        ELSE I.RetnDate
    END AS RDatev,
    I.bit_field_v41 AS bitField,
    I.FirstDate AS FDatev,
    I.BookDate AS DBooked,
    I.TimeBookedH AS TBookH,
    I.TimeBookedM AS TBookM,
    I.TimeBookedS AS TBookS,
    I.del_time_hour AS dth,
    I.del_time_min AS dtm,
    I.return_to_locn AS rtlocn,
    I.return_time_hour AS rth,
    I.return_time_min AS rtm,
    CASE
        WHEN
            I.Trans_type_v41 IN (6, 7) 
            AND I.Trans_qty < I.QtyCheckedOut
            THEN 0 
        WHEN
            I.Trans_type_v41 IN (6, 7)
            AND I.Trans_qty >= I.QtyCheckedOut
            THEN I.Trans_Qty - I.QtyCheckedOut
        ELSE
            I.trans_qty
    END AS trqty,
    CASE
        WHEN I.Trans_type_v41 IN (6, 7)
        THEN 0
        ELSE I.QtyCheckedOut
    END AS MyQtycheckedout,
    CASE
        WHEN I.Trans_type_v41 IN (6, 7)
        THEN 0
        ELSE I.QtyReturned
    END AS retqty,
    I.ID,
    B.BookingProgressStatus AS bkProg,
    I.product_code_v42,
    I.return_to_locn,
    I.AssignTo,
    I.AssignType,
    I.QtyReserved,
    B.DeprepOn,
    CASE B.DeprepOn
        WHEN 1 THEN B.DeprepDateTime
        ELSE I.RetnDate
    END AS DeprepDateTime,
    I.InRack
FROM dbo.V1 AS I WITH (NOEXPAND)
JOIN dbo.tblbookings AS B ON
    B.booking_no = I.booking_no_v32
JOIN dbo.tblInvmas AS M ON
    I.product_code_v42 = M.product_code;

Пример запроса и план выполнения:

SELECT
    vrsv.*
FROM dbo.vwReallySlowView2 AS vrsv
WHERE vrsv.product_code_v42 = 'M10BOLT';

Новый план исполнения

В новом плане у совпадения хеш-функции нет остаточного предиката , нет сложного фильтра , нет остаточного предиката при поиске в индексированном представлении, и оценки количества элементов являются точными.

В качестве примера того, как будут затронуты планы вставки / обновления / удаления, это план для вставки в таблицу ItemTrans:

Вставить план

Выделенный раздел является новым и необходим для ведения индексированного представления. Буферная таблица воспроизводит вставленные строки базовой таблицы для обслуживания индексированного представления. Каждая строка присоединяется к таблице заказов с использованием поиска по кластерному индексу, затем фильтр применяет WHEREпредикаты сложного предложения, чтобы увидеть, нужно ли добавить строку в представление. Если это так, вставка выполняется в кластеризованный индекс представления.

Тот же SELECT * FROM viewтест, выполненный ранее, завершился через 150 мс с индексированным представлением на месте.

И последнее: я заметил, что ваш сервер 2008 R2 все еще работает на RTM. Это не исправит ваши проблемы с производительностью, но Service Pack 2 для 2008 R2 был доступен с июля 2012 года, и есть много веских причин, чтобы как можно более актуально использовать пакеты обновления.

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