Код создает другой план при запуске ad-hoc против хранимой процедуры

9

У меня есть оператор удаления, который использует неверный план при запуске внутри хранимой процедуры, но выбирает гораздо лучший план при запуске ad-hoc.

Я перестроил все индексы для таблиц, используемых запросом, и удалил все кэши. Оптимизатор по-прежнему выбирает неправильный план для хранимой процедуры.

Я хотел бы знать, почему оптимизатор использует другой план выполнения для хранимой процедуры в отличие от специального SQL.

ОБНОВЛЕНИЕ: я думаю, что это, должно быть, параметры в конце концов - когда я запустил специальный код с жестко закодированной переменной, я могу получить «плохой» план с правильным значением (это дата, значения, которые годичны похоже на генерацию "хорошего" плана). Теперь перейдем к попытке навязать «хороший» план процессу с помощью подсказки запроса.

РЕШЕНИЕ: я получил план, который хотел, используя подсказку ОПТИМИЗАЦИЯ ДЛЯ НЕИЗВЕСТНОГО.

msgisme
источник
Это, вероятно, сниффинг параметров . Запрос ссылается на переменную в своем WHEREпредложении?
Ник Чаммас
4
Можете ли вы добавить код, пожалуйста
gbn
Также разместите планы где-нибудь, пожалуйста. Как это будет довольно сложно сказать вам, почему планы разные.
Аарон Бертран
Хорошо, больше информации: я не могу опубликовать планы и коды, пока не запутаю их немного. Я постараюсь поднять их в несколько раз. План для хранимой процедуры (плохой) выполняет сканирование кластерного индекса большой таблицы (целиком, всех разделов). Затем он использует цикл для поиска строк из таблицы меньшего размера, а затем удаляет из таблицы меньшего размера.
msgisme
План для специального кода (хорошо) выполняет сканирование таблицы небольшой таблицы (в которой всего 5-10 строк) и использует некластеризованный индекс большой таблицы, чтобы найти, какие строки нужно проверить в ПК. большого стола. Я постараюсь раскрыть реальные планы, как только смогу.
msgisme

Ответы:

5

Обычные подозреваемые:

  1. константы в adhoc, параметры в коде
  2. несоответствие типов данных в коде
  3. сниффинг параметров

Точка 1: оптимизатор может выбрать лучший план для констант.
Изменить константы = изменить план. Параметризованный плен можно восстановить

Точка 2 вводит неявные преобразования из-за приоритета типа данных,
например столбец varchar по сравнению с параметром nvarchar

Точка 3: используйте маскировку параметров или OPTIMIZE FOR UNKNOWN.
Редактирование: Для проверки: запустите сохраненный процесс, запустите sp_updatestats, запустите снова. Это сделает недействительными кэшированные планы, что лучше, чем очистка кэша планов

Изменить: после комментария jcolebrand

Вы можете отключить сниффинг несколькими способами. Основными 3 являются

  • RECOMPILE. Это глупое ИМО.
  • ОПТИМИЗИРУЙТЕ (так) НЕИЗВЕСТНО
  • Маскировка параметров

Маскировка параметров:

DECLARE @MaskedParam varchar(10)
SELECT @MaskedParam = @SignaureParam

SELECT...WHERE column = @MaskedParam

Маскировка и подсказка ОПТИМИЗАЦИЯ имеют одинаковый эффект (возможно, по разным причинам). То есть оптимизатор должен использовать статистику и распределение данных ( Примечание: все еще тестируется Марком Стори-Смитом ), чтобы оценить параметры по их собственным достоинствам ? , а не то, что они были в последний раз. Оптимизатор может перекомпилировать или нет. В SQL Server 2005 добавлена ​​перекомпиляция на уровне операторов, чтобы уменьшить влияние

Теперь, почему план с «вынюхиваемыми» параметрами является «липким» по сравнению с замаскированными / «неизвестными» параметрами, я не уверен.

Я использовал маскирование параметров начиная с SQL Server 2000 для всего, кроме самого простого кода. Я заметил, что это может произойти с более сложным кодом. А на моей старой работе у меня есть несколько процедур отчетов, которые я могу изменить по умолчанию для параметров плана. Я считаю, что подход "культа груза" был проще, чем звонок в службу поддержки.

Изменить 2, 12 октября 2011, после некоторого чата

  • Насколько я могу судить, маскирование параметров и ОПТИМИЗАЦИЯ ДЛЯ НЕИЗВЕСТНО имеют тот же эффект
    . Подсказка более чистая, чем маскировка, но была добавлена ​​в SQL Server 2008.

  • Обнаружение параметров происходит во время компиляции.
    С RECOMPILE генерирует новый план при каждом выполнении. Это означает, что плохой выбор значений по умолчанию повлияет на план. На моей последней работе я мог легко продемонстрировать это с помощью некоторого кода отчета: изменение параметров по умолчанию изменило план независимо от предоставленных параметров.

  • Эта статья о MS Connect интересна: субоптимальное использование индекса в хранимой процедуре (упомянуто в одном из ответов SO ниже)

  • Боб Боучемин тоже об этом упоминает

Нерешенные вопросы

  • Применяется ли сниффинг с RECOMPILE? То есть, если оптимизатор знает, что отказаться от плана, он стремится к повторному использованию?

  • Почему обнюхиваемые планы "липкие"?

Ссылки с SO:

ГБН
источник
1. Параметр в sp - это переменная в коде 2. Опять тот же тип данных 3. Я запустил оба с большим количеством параметров и каждый раз получаю один и тот же план. Я очищал кеш после каждой попытки.
msgisme
1
По пункту 3. Вы также можете запустить оператор с OPTION (RECOMPILE)или весь процесс, WITH RECOMPILEчтобы заставить SQL Server игнорировать существующие планы.
Ник Чаммас
3
Кстати OPTIMIZE, потому что Microsoft - американская компания. :)
Ник Чаммас
1
@Gbn какие-нибудь мысли о предотвращении / устранении перехвата параметров?
Jcolebrand
1
@jcolebrand: простой ответ «нет» :-)
gbn
2

Не забывайте, что настройки ANSI, которые вы настроили для плана соединений, играют роль в выборе плана выполнения. Когда приложение вызывает хранимую процедуру, оно, вероятно, имеет другие настройки ANSI, чем ваше соединение SSMS.

mrdenny
источник