У мене є оператор SQL UPDATE з пунктом "TOP (X)", і рядок, в якому я оновлюю значення, містить близько 4 мільярдів рядків. Коли я використовую "TOP (10)", я отримую один план виконання, який виконується майже миттєво, але коли я використовую "TOP (50)" або більше, запит ніколи (принаймні, не під час очікування) закінчується, і він використовує зовсім інший план виконання. Менший запит використовує дуже простий план з парою шукає індекс і вкладений вкладений цикл, де для того самого запиту (з різною кількістю рядків у пункті TOP оператора UPDATE) використовується план, який включає дві різні пошуки індексу , котушка столу, паралелізм і купа інших складностей.
Я використав "OPTION (USE PLAN ...)", щоб змусити його використовувати план виконання, сформований меншим запитом - коли я це роблю, я можу оновити цілих 100 000 рядків за кілька секунд. Я знаю, що план запитів хороший, але SQL Server вибере цей план самостійно лише тоді, коли буде задіяна лише невелика кількість рядків - будь-яка пристойно велика кількість рядків у моєму оновленні призведе до неоптимального плану.
Я подумав, що в цьому може бути винен паралелізм, тому я став MAXDOP 1
на запит, але безрезультатно - цього кроку вже немає, але поганий вибір / ефективність - ні. Я також побіг sp_updatestats
цього ранку, щоб переконатися, що це не було причиною.
Я додав два плани виконання - коротший - і швидший. Крім того, ось питання, про який йдеться (варто зазначити, що SELECT, який я включив, здається, швидкий у випадках як малого, так і великого числа рядків):
update top (10000) FactSubscriberUsage3
set AccountID = sma.CustomerID
--select top 50 f.AccountID, sma.CustomerID
from FactSubscriberUsage3 f
join dimTime t
on f.TimeID = t.TimeID
join #mac sma
on f.macid = sma.macid
and t.TimeValue between sma.StartDate and sma.enddate
where f.AccountID = 0 --There's a filtered index on the table for this
Чи є щось очевидне в тому, як я налаштовую свій запит, або в плані виконання, за умови, що піддається поганому вибору механізму запитів? Якщо необхідно, я можу також включити визначення таблиць та індекси, які визначені на них.
Тим, хто запитував статистичну версію об’єктів бази даних: Я навіть не розумів, що ти можеш це зробити, але це має повний сенс! Я намагався генерувати скрипти для бази даних, що мають лише статистику, щоб інші могли перевірити плани виконання для себе, але я можу генерувати статистику / гістограми на моєму відфільтрованому індексі (помилка синтаксису в скрипті, здається), тому я від удачі там. Я спробував зняти фільтр, і плани запитів були близькими, але не зовсім однаковими, і я не хочу нікого надсилати на гусячу погоню.
Оновлення та ще кілька повних планів виконання: Перш за все, SQL Sentry's Plan Explorer - це неймовірний інструмент. Я навіть не знав, що він існує, поки не переглянув інші запитання щодо плану запитів на цьому веб-сайті, і мені було досить багато сказати про те, як виконуються мої запити. Хоча я не впевнений, як вирішити проблему, вони дали зрозуміти, у чому проблема.
Ось підсумок 10, 100 та 1000 рядків - ви можете бачити, що запит у 1000 рядків - це спосіб, виходячи з решти інших:
Ви можете бачити, що третій запит має смішну кількість прочитаних, тому він, очевидно, робить щось зовсім інше. Ось передбачуваний план виконання з підрахунками рядків. Розрахунковий план виконання на 1000 рядків:
І ось фактичні результати плану виконання (до речі, під "ніколи не закінчується", виявляється, я мав на увазі "закінчується за годину"). Фактичний план виконання 1000 рядків
Перше , що я помітив, що, замість того , щоб тягнути 60 тисяч рядків з таблиці dimTime , як він очікує, що це на самому справі тягне 1,6 млрд, з B . Дивлячись на мій запит, я не впевнений, як це відтягує стільки рядків із таблиці dimTime. Оператор BETWEEN, який я використовую, просто гарантує, що я витягую правильний запис з #mac на основі запису часу в таблиці Фактів. Однак, коли я додаю рядок до пункту WHERE, де я фільтрую t.TimeValue (або t.TimeID) до одного значення, я можу успішно оновити 100000 рядків за лічені секунди. В результаті цього, і як було зрозуміло в планах виконання, які я включив, очевидно, що проблема в моєму графіку роботи, але я не впевнений, як я змінив би критерії приєднання, щоб вирішити цю проблему і підтримувати точність . Будь-які думки?
Для довідки, ось план (з кількістю рядків) для оновлення 100 рядків. Ви можете бачити, що він потрапляє в той самий індекс і все ще з тоною рядків, але ніде не має однакової величини проблеми. Виконання 100 рядків із кількістю ліній :
from #mac sma join f on f.macid = sma.macid join dimTime t on f.TimeID = t.TimeID and t.TimeValue between sma.StartDate and sma.enddate
протиfrom #mac join t on t.TimeValue between sma.StartDate and sma.enddate join f on f.TimeID = t.TimeID and f.macid = sma.macid
TOP 50
все ж слід швидко виконати. Чи можете ви завантажити плани XML? Мені потрібно переглянути кількість рядків. Чи можете ви запустити TOP 50
з maxdop 1 і як вибір, а не як оновлення та розмістити план? (Спрощення спрощення / розбиття простору пошуку).
t.TimeValue between sma.StartDate and sma.enddate
може в кінцевому підсумку створити набагато більше марних рядків, які згодом відфільтруються в ході з'єднання проти FactSubscriber і тому не закінчуються в кінцевому результаті.
sp_updatestatistics
по столу?