План виконання показує дорогу операцію CONVERT_IMPLICIT. Чи можна це виправити за допомогою індексації чи мені потрібно змінити таблицю?


24

У мене дуже важливий, дуже повільний погляд, який включає деякі по-справжньому потворні умови на кшталт цього у пункті «де». Я також усвідомлюю, що об'єднання - це грубе і повільне приєднання 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 або в роботі типу DBA в цілому, так як я більшу частину часу не розробник програмного забезпечення, що не є 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предиката. оновлення Зверніть увагу, що в цьому випадку мій найгірший вузол зараз "хеш-матч" на inner join, який коштує 34%. Я вважаю, що це вартість, якої я не можу уникнути, якщо я не зможу уникнути приєднання до рядків, які я зараз не можу позбуватися. Обидві INNER JOINоперації у поданому вище вікні виконаніvarchar(13) полях.

Збільшений масштаб у правому нижньому куті:

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

Весь план виконання як .sqlplan доступний у програмі skydrive. Це зображення є лише візуальним оглядом. Клацніть тут, щоб побачити зображення самостійно.

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

Оновлення розміщено весь план виконання. Я, здається, не знаю, яке product_codeзначення було патологічно поганим, але один із способів зробити це - select count(*) from viewзамість того, щоб робити один продукт. Але продукти, які використовуються лише у 5% записів у нижній таблиці або менше, здаються, мають значно менші витрати в процесі CONVERT_IMPLICIT експлуатації. Якби я збирався виправити SQL тут, я думаю, я взяв би грубе WHEREзастереження у поданні та обчислював і зберігав результат цього гігантського де-умова як умовне бітове поле "IncludeMeInTheView" у нижній таблиці . Престо, проблема вирішена, правда?



Опублікований я .SQLPLAN набагато менш патологічний, ніж випадок 432k / 17k на оригінальному зображенні. Прошу вибачення, що більше не можу знайти product_codeзначення, яке я використав для створення цього патологічного 87%випадку за допомогою моїх даних. Зображення тепер показують 27%. Знову вибачення за плутанину через мої зміни.
Warren P

Відповіді:


49

Не слід занадто сильно покладатися на відсоток витрат у планах виконання. Це завжди кошторисні витрати , навіть у планах після виконання з "фактичними" номерами для речей, таких як кількість рядків. Орієнтовні витрати ґрунтуються на моделі, яка працює досить добре з метою, для якої вона призначена: надання можливості оптимізатору вибирати між різними планами виконання кандидатів для одного запиту. Інформація про вартість цікава та важливий фактор, але вона рідко повинна бути основним показником для настройки запитів. Інтерпретація інформації плану виконання вимагає більш широкого перегляду представлених даних.

ItemTran Clustered Index Шукайте оператора

ItemTran Clustered Index See

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

Перетворення відбувається тому, що побітовий оператор AND (&) вимагає, щоб обидва операнди були одного типу. Неявний тип постійного значення "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 Data Generator . Використовуючи ті ж підрахунки рядків таблиці, як показано в плані запитів запитання, сильно фрагментований кластерний індекс призвів до SELECT * FROM view15 секунд післяDBCC DROPCLEANBUFFERS . Той самий тест у тих самих умовах із щойно відремонтованим кластерним індексом у таблиці ItemTrans завершений за 3 секунди.

Якщо дані таблиці, як правило, повністю знаходяться в кеші, проблема фрагментації дуже менш важлива. Але навіть при низькій фрагментації широкі рядки таблиці можуть означати, що кількість логічних та фізичних зчитувань значно більша, ніж можна було очікувати. Ви також можете експериментувати з додаванням та видаленням явного, CONVERTщоб підтвердити моє сподівання, що проблема неявного перетворення тут не важлива, за винятком випадків порушення кращої практики.

Більш суттєвим є приблизна кількість рядків, що залишають оператора пошуку. Оцінка часу оптимізації становить 165 рядків, але на час виконання було вироблено 4226. Я повернусь до цього пункту пізніше, але головна причина розбіжності полягає в тому, що вибірковість залишкового предиката (за участю побітових AND) дуже важко передбачити оптимізатору - адже він вдається до здогадок.

Оператор фільтру

Оператор фільтру

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

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))

Оператор відповідності Hash

Виконання з'єднання типів символьних даних не є причиною високої оціночної вартості цього оператора. Підказка про SSMS показує лише запис зонду хеш-ключів, але важливі деталі містяться у вікні властивостей SSMS.

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

Залишковий зонд подібний до Залишкового, який спостерігається після пошуку індексу раніше; залишковий предикат оцінюється у всіх рядках, що відповідають хешу, щоб визначити, чи слід передавати рядок батьківському оператору. Пошук відповідних хеш-таблиць у добре збалансованій хеш-таблиці є надзвичайно швидким, але застосування складного залишкового предиката до кожного ряду, який відповідає, порівняно досить повільний. Підказка Hash Match в Провіднику плану показує деталі, включаючи залишковий вираз зонда:

Оператор матчу

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

Недооцінка кількості рядків є проблемою для оператора Hash Match, оскільки обсяг пам’яті, зарезервований для хеш-таблиці, заснований на передбачуваній кількості рядків. Якщо пам'ять занадто мала для розміру хеш-таблиці, необхідної під час виконання (через більшу кількість рядків) хеш-таблиця рекурсивно переливається на фізичне зберігання tempdb , що часто призводить до дуже низької продуктивності. У гіршому випадку двигун виконання припиняє рекурсивно розливати хеш-відра і вдається до a дуже повільноалгоритм порятунку Розлив хешу (рекурсивний або рятувальний) є найбільш ймовірною причиною проблем із ефективністю, викладених у запитанні (не стовпчики приєднання типу символів чи неявні перетворення). Першопричиною буде те, що сервер резервує занадто мало пам’яті для запиту, що базується на оцінці неправильного підрахунку рядків (кардинальності).

На жаль, перед SQL Server 2012 в плані виконання немає вказівки на те, що операція хешування перевищила розподіл пам’яті (яка не може динамічно зростати після резервування перед початком виконання, навіть якщо на сервері є маса вільної пам’яті) і довелося перелитись на tempdb. Можливо відстежувати клас подій Hash Warning за допомогою 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:

Вставте план

Виділений розділ новий та необхідний для обслуговування індексованого перегляду. Котушка таблиці відтворює вставлені рядки базової таблиці для обслуговування індексованого перегляду. Кожен рядок приєднується до таблиці бронювання за допомогою кластеризованого індексу search, тоді фільтр застосовує складні WHEREпредикативні пропозиції, щоб побачити, чи потрібно додавати рядок до перегляду. Якщо так, вкладений вкладок до кластерного індексу подання.

Той самий SELECT * FROM viewтест, виконаний раніше, виконаний за 150 мс, з індексованим видом на місці.

Заключна річ: я помічаю, що ваш сервер R2 2008 року все ще знаходиться на RTM. Це не виправить ваші проблеми з продуктивністю, але пакет оновлень 2 для 2008 року R2 доступний з липня 2012 року, і є безліч вагомих причин залишатися максимально актуальними з пакетами послуг.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.