(Вопрос перенесен с ТАК)
У меня таблица (фиктивные данные) с кластерным индексом содержит 2 столбца:
Теперь я запускаю эти два запроса:
declare
@productid int =1 ,
@priceid int = 1
SELECT productid,
t.priceID
FROM Transactions AS t
WHERE (productID = @productid OR @productid IS NULL)
AND (priceid = @priceid OR @priceid IS NULL)
SELECT productid,
t.priceID
FROM Transactions AS t
WHERE (productID = @productid)
AND (priceid = @priceid)
Фактический план выполнения для обоих запросов:
Как видите, первый использует SCAN, а второй использует SEEK.
Однако - добавив OPTION (RECOMPILE)
к первому запросу, составил план выполнения и для использования SEEK:
Друзья в чате DBA сказали мне, что:
В вашем запросе @ productid = 1, что означает, что (productID = @ productID OR @productID IS NULL) можно упростить до (productID = @ productID). Первый требует сканирования для работы с любым значением @productID, второй может использовать поиск. Поэтому, когда вы используете RECOMPILE, SQL Server посмотрит, какое значение вы на самом деле имеете в @productID, и составит лучший план для него. При ненулевом значении в @productID поиск является лучшим. Если значение @productID неизвестно, план должен соответствовать любому возможному значению в @productID, что потребует сканирования. Имейте в виду: OPTION (RECOMPILE) будет принудительно перекомпилировать план при каждом его запуске, что будет добавлять несколько миллисекунд к каждому выполнению. Хотя это проблема, только если запрос выполняется очень часто.
Также :
Если @productID равен нулю, какое значение вы бы искали? Ответ: искать нечего. Все значения соответствуют требованиям.
Я понимаю, что OPTION (RECOMPILE)
вынуждает SQL Server видеть, какие фактические значения имеют параметры, и посмотреть, может ли он с ним ПОИСКАТЬ.
Но теперь я теряю преимущество предварительной компиляции.
Вопрос
ИМХО - СКАНИРОВАНИЕ произойдет, только если параметр равен нулю.
Это нормально - пусть SQL SERVER создаст план выполнения для SCAN.
НО, если SQL Server видит, что я выполняю этот запрос много раз со значениями: 1,1
почему тогда он не создает ДРУГОЙ план выполнения и не использует для этого SEEK?
AFAIK - SQL создает план выполнения для наиболее популярных запросов .
Почему SQL SERVER не сохраняет план выполнения для:
@productid int =1 , @priceid int = 1
(Я запускаю его много раз с этими значениями)
- Можно ли заставить SQL сохранить этот план выполнения (который использует SEEK) - для будущего вызова?
источник
Ответы:
Подводя итог некоторым из основных моментов нашего обсуждения в чате :
Вообще говоря, SQL Server кэширует отдельный план для каждого оператора . Этот план должен быть действительным для всех возможных будущих значений параметров .
Невозможно кэшировать план поиска для вашего запроса, потому что этот план будет недействительным, если, например, @productid имеет значение null.
В некоторых будущих выпусках SQL Server может поддерживать один план, который динамически выбирает между сканированием и поиском, в зависимости от значений параметров времени выполнения, но это не то, что мы имеем сегодня.
Общий класс проблемы
Ваш запрос является примером шаблона, по-разному называемого запросом «поймать все» или «динамический поиск». Существуют различные решения, каждое из которых имеет свои преимущества и недостатки. В современных версиях SQL Server (2008+) основными параметрами являются:
IF
блокиOPTION (RECOMPILE)
sp_executesql
Наиболее полная работа по этой теме, вероятно, принадлежит Эрланду Соммарскогу, который включен в ссылки в конце этого ответа. От сложностей не уйти, поэтому необходимо потратить некоторое время на то, чтобы опробовать каждый вариант, чтобы понять компромиссы в каждом конкретном случае.
IF
блокиЧтобы проиллюстрировать
IF
блочное решение для конкретного случая в вопросе:Он содержит отдельное утверждение для четырех возможных случаев, когда значение равно нулю или не равно нулю для каждого из двух параметров (или локальных переменных), поэтому существует четыре плана.
Существует потенциальная проблема с анализом параметров, которая может потребовать
OPTIMIZE FOR
подсказки для каждого запроса. Пожалуйста, ознакомьтесь с разделом ссылок, чтобы изучить эти типы тонкостей.Рекомпилированные
Как отмечено выше в вопросе, вы также можете добавить
OPTION (RECOMPILE)
подсказку, чтобы получить новый план (поиск или сканирование) при каждом вызове. Учитывая относительно медленную частоту вызовов в вашем случае (в среднем каждые десять секунд, со временем компиляции менее миллисекунды), вероятно, этот вариант подойдет вам:Кроме того, можно творчески комбинировать функции из вышеперечисленных вариантов, чтобы максимально использовать преимущества каждого из методов, одновременно сводя к минимуму недостатки. На самом деле нет никакого способа понять все это подробно, а затем сделать осознанный выбор, подкрепленный реалистичным тестированием.
дальнейшее чтение
RECOMPILE
параметрыисточник