У мене є дещо складний запит SQL Server 2008 (близько 200 рядків досить щільного SQL), який не працював так, як мені потрібно. З часом продуктивність знизилася з приблизно .5 до приблизно 2 секунд.
Поглянувши на план виконання, було досить очевидно, що, переупорядкувавши з'єднання, можна підвищити ефективність роботи. Я так і зробив ... аж до .3 секунди. Тепер запит має підказку "ЗАМОВЛЕННЯ ВАРІТТЯ СИЛИ" , і життя - це добре.
Сьогодні приходить мене, прибираючи базу даних. Я архівувати близько 20% рядків, не беручи ніяких дій у відповідній базі даних , за винятком видалення рядків ... план виконання отримує ПОВНІСТЮ обливали . Він повністю неправильно оцінює, скільки рядків повернеться певних підрядків, і (наприклад) замінює:
<Hash>
з
<NestedLoops Optimized='false' WithUnorderedPrefetch='true'>
Зараз час запиту зменшується приблизно від .3 до приблизно 18-х. (!) Просто тому, що я видалив рядки. Якщо я видаляю підказку запиту, я повернусь до часу запиту приблизно 2 секунди. Краще, але гірше.
Я відтворив проблему після відновлення бази даних на декількох місцях і серверах. Просто видалення приблизно 20% рядків з кожної таблиці завжди викликає цю проблему.
- Чи нормально це для порядку примусового приєднання, щоб зробити оцінки запиту абсолютно неточними (і, таким чином, час запитів непередбачуваним)?
- Чи варто просто очікувати, що мені доведеться або приймати недооптимальну ефективність запиту, або дивитися на нього як на яструба і часто вручну редагувати підказки запитів? Чи, можливо, натякають і на кожне приєднання? .3s до 2s - великий удар.
- Чи очевидно, чому оптимізатор підірвався після видалення рядків? Наприклад, "так, було проведено вибіркове сканування, і тому, що я архівував більшість рядків раніше в історії даних, зразок давав розріджені результати, тож він недооцінював потребу в відсортованій хеш-операції"?
Якщо ви хочете побачити плани виконання, запропонуйте, де я можу розмістити їх. Інакше я пробував найдивовижніший шматочок. Ось основна помилкова оцінка, числа в паренах складають (оціночні: фактичні) рядки.
/ Clustered Index Scan (908:7229)
Nested Loops (Inner Join) --<
\ NonClustered Index Seek (1:7229)
Зверніть увагу, що внутрішній цикл повинен сканувати 908 рядків, але замість цього сканує 52 258 441. Якби це було точно, ця гілка пробігла б приблизно 2 мс, а не 12 сек. Перед видаленням рядків ця внутрішня оцінка приєднання була виключена лише загальним коефіцієнтом 2 і виконувалася як хеш-відповідність на двох кластерних індексах.