У меня есть оператор 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
Есть ли что-то очевидное в том, как я настраиваю свой запрос, или в плане выполнения, при условии, что он может быть ошибочным выбором, который делает механизм запросов? При необходимости я также могу включить соответствующие определения таблиц и индексы, которые для них определены.
Для тех, кто просил версию объектов базы данных только для статистики: я даже не осознавал, что вы можете это сделать, но это имеет смысл! Я попытался сгенерировать сценарии для базы данных только для статистики, чтобы другие могли сами проверить планы выполнения, но я могу сгенерировать статистику / гистограммы на моем отфильтрованном индексе (похоже, ошибка синтаксиса в сценарии), поэтому я не повезло там. Я попытался удалить фильтр, и планы запросов были близки, но не совсем одинаковы, и я не хочу никого отправлять в погоню.
Обновление и некоторые более полные планы выполнения: во- первых, Plan Plan в SQL Sentry - невероятный инструмент. Я даже не знал, что он существует, пока не просмотрел другие вопросы о плане запросов на этом сайте, и он мог сказать немного о том, как выполнялись мои запросы. Хотя я не уверен, как решить проблему, они сделали очевидным, в чем проблема.
Вот сводка для 10, 100 и 1000 строк - вы можете видеть, что запрос 1000 строк - это путь далеко от других:
Вы можете видеть, что третий запрос имеет смехотворное количество операций чтения, поэтому он явно выполняет что-то совершенно другое. Вот примерный план выполнения с количеством строк. 1000-рядный примерный план выполнения:
И вот фактические результаты плана выполнения (кстати, под словом «никогда не заканчивается», оказывается, я имел в виду «заканчивается через час»). 1000-рядный план фактического выполнения
Первое , что я заметил, что, вместо того , чтобы тянуть 60 тысяч строк из таблицы dimTime , как он ожидает, что это на самом деле тянет 1,6 млрд, с B . Глядя на мой запрос, я не уверен, как он вытягивает столько строк из таблицы dimTime. Оператор BETWEEN, который я использую, просто гарантирует, что я извлекаю правильную запись из #mac на основе записи времени в таблице Fact. Однако, когда я добавляю строку к предложению WHERE, где я фильтрую t.TimeValue (или t.TimeID) к одному значению, я могу успешно обновить 100 000 строк за считанные секунды. В результате этого, и как ясно показано в планах выполнения, которые я включил, очевидно, что проблема заключается в моем расписании, но я не уверен, как бы я изменил критерии объединения, чтобы обойти эту проблему и сохранить точность , Есть предположения?
Для справки, здесь план (с количеством строк) для обновления 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
на столе?