T-SQL запрос использует совершенно другой план в зависимости от количества строк, которые я обновляю


20

У меня есть оператор 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-строчный примерный план выполнения

И вот фактические результаты плана выполнения (кстати, под словом «никогда не заканчивается», оказывается, я имел в виду «заканчивается через час»). 1000-рядный план фактического выполнения 1000-рядный план фактического выполнения

Первое , что я заметил, что, вместо того , чтобы тянуть 60 тысяч строк из таблицы dimTime , как он ожидает, что это на самом деле тянет 1,6 млрд, с B . Глядя на мой запрос, я не уверен, как он вытягивает столько строк из таблицы dimTime. Оператор BETWEEN, который я использую, просто гарантирует, что я извлекаю правильную запись из #mac на основе записи времени в таблице Fact. Однако, когда я добавляю строку к предложению WHERE, где я фильтрую t.TimeValue (или t.TimeID) к одному значению, я могу успешно обновить 100 000 строк за считанные секунды. В результате этого, и как ясно показано в планах выполнения, которые я включил, очевидно, что проблема заключается в моем расписании, но я не уверен, как бы я изменил критерии объединения, чтобы обойти эту проблему и сохранить точность , Есть предположения?

Для справки, здесь план (с количеством строк) для обновления 100 строк. Вы можете видеть, что он достигает того же индекса, и все еще с тонной строк, но нигде не имеет такой же масштаб проблемы. Выполнение 100 строк с количеством строк : введите описание изображения здесь


Это должно быть статистика. Вы запускаете sp_updatestatisticsна столе?
JNK

@JNK: Первоначально я так думал, но уже запустил sp_updatestats без изменений. Я просто запустил его снова, и ему было все равно обновлять статистику по любому из индексов, задействованных в запросе. Спасибо хоть!
SqlRyan

Второй - это широкий (по индексу) план обновления, а не узкий (по строке) план, объясняющий некоторые дополнительные видимые сложности. Но на самом деле единственное различие заключается в порядке соединения 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 и как выбор, а не как обновление и опубликовать план? (Попытка упростить / разделить пространство поиска).
USR

Присоединение @usr t.TimeValue between sma.StartDate and sma.enddateможет привести к созданию гораздо большего количества бесполезных строк, которые впоследствии будут отфильтрованы в объединении с FactSubscriber и, следовательно, не окажутся в конечном результате.
Мартин Смит

Ответы:


3

Индекс на dimTime меняется. Более быстрый план использует индекс _dta. Прежде всего, убедитесь, что он не помечен как гипотетический индекс в sys.indexes.

Думая, что вы могли бы обойти некоторую параметризацию, используя таблицу #mac для фильтрации, вместо того, чтобы просто указывать даты начала / окончания, такие как WHERE t.TimeValue между @StartDate и @enddate. Избавьтесь от этой временной таблицы.


Индекс с префиксом dta выглядит так, как будто он был создан в соответствии с рекомендациями DTA без изменения имени. Гипотетические индексы не могут появляться в реальных планах выполнения (и не будут оцениваться без некоторых недокументированных команд). Не уверен, как ваше второе предложение будет работать. t.TimeValue between sma.StartDate and sma.enddateкоррелируется, поэтому может меняться для каждой строки в #tempтаблице. Чем бы ОП заменил его?
Мартин Смит

Достаточно справедливо, я не обращал достаточно внимания на временную таблицу.
william_a_dba

1
Тем не менее, гипотетические индексы действительно могут испортить план выполнения. Если это гипотетически, его следует отбросить и воссоздать. blogs.technet.com/b/anurag_sharma/archive/2008/04/15/…
william_a_dba

Гипотетические индексы остаются, когда DTA не завершает / останавливается до завершения. Вы должны очистить их вручную, если есть какие-либо сбой с DTA.
william_a_dba

1
@william_a_dba - Ах, теперь я понимаю, что вы имеете в виду (после прочтения вашей ссылки). Запрос никогда не завершается, возможно, он постоянно перекомпилируется. Интересный!
Мартин Смит

1

Без дополнительной информации о количестве строк в плане моя предварительная рекомендация - установить правильный порядок соединения в запросе и принудительно его использовать OPTION (FORCE ORDER). Обеспечить порядок соединения первого плана.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.