Есть ли способ отложить компиляцию плана выполнения хранимой процедуры? - PullRequest
2 голосов
/ 15 апреля 2010

(На первый взгляд это может выглядеть как дубликат Другой план выполнения при выполнении оператора непосредственно и из хранимой процедуры или Почему оптимизатор SqlServer так запутывается с параметрами? , но мой фактический вопрос немного другой)

Хорошо, из-за этого меня несколько часов ставили в тупик. Мой пример здесь смехотворно абстрагирован, поэтому я сомневаюсь, что можно будет воссоздать его локально, но он предоставляет контекст для моего вопроса (также я использую SQL Server 2005).

У меня есть хранимая процедура, состоящая в основном из двух шагов: создание временной таблицы, заполнение ее очень небольшим количеством строк и запрос к очень большой таблице, соединяющейся с этой временной таблицей. Он имеет несколько параметров, но наиболее актуальным является datetime "@MinDate." По существу:

create table #smallTable (ID int)

insert into #smallTable
select (a very small number of rows from some other table)

select * from aGiantTable
inner join #smallTable on #smallTable.ID = aGiantTable.ID
inner join anotherTable on anotherTable.GiantID = aGiantTable.ID
where aGiantTable.SomeDateField > @MinDate

Если я просто выполню это как обычный запрос, объявив @MinDate как локальную переменную и запустив ее, он создаст оптимальный план выполнения, который выполняется очень быстро (сначала объединяется в #smallTable, а затем рассматривает только очень маленькое подмножество строк из aGiantTable при выполнении других операций). Кажется, вы понимаете, что #smallTable очень маленький, поэтому было бы полезно начать с него. Это хорошо.

Однако, если я сделаю это хранимой процедурой с @MinDate в качестве параметра, это приведет к совершенно неэффективному плану выполнения. (Я перекомпилирую его каждый раз, так что это не плохой кешированный план ... по крайней мере, я уверен, что это не так)

Но вот где это становится странным. Если я изменю proc на следующее:

declare @LocalMinDate datetime
set @LocalMinDate = @MinDate --where @MinDate is still a parameter

create table #smallTable (ID int)

insert into #smallTable
select (a very small number of rows from some other table)

select * from aGiantTable
inner join #smallTable on #smallTable.ID = aGiantTable.ID
inner join anotherTable on anotherTable.GiantID = aGiantTable.ID
where aGiantTable.SomeDateField > @LocalMinDate

Тогда это дает мне эффективный план!


Итак, моя теория такова: при выполнении в виде простого запроса (а не в виде хранимой процедуры) он ожидает построения плана выполнения для дорогого запроса до последней минуты, поэтому оптимизатор запросов знает, что #smallTable мал и использует эту информацию для составления эффективного плана.

Но при выполнении в виде хранимой процедуры он сразу создает весь план выполнения, поэтому он не может использовать этот бит информации для оптимизации плана.

Но почему использование локально объявленных переменных меняет это? Почему это задерживает создание плана выполнения? Это на самом деле то, что происходит? Если да, есть ли способ принудительно отложить компиляцию (если это действительно происходит здесь), даже если не использовать локальные переменные таким образом?

В более общем смысле, есть ли у кого-нибудь источники о том, когда создается план выполнения для каждого шага хранимой процедуры? Google не предоставил никакой полезной информации, но я не думаю, что ищу правильную вещь. Или моя теория просто совершенно необоснованна?

Редактировать : Со времени публикации я узнал о сниффинге параметров, и я предполагаю, что именно это приводит к преждевременной компиляции плана выполнения (если только хранимые процедуры не скомпилируют все сразу), поэтому мой вопрос остается - Вы можете заставить задержку? Или полностью отключить сниффинг?

Вопрос носит академический характер, поскольку я могу разработать более эффективный план, заменив select * from aGiantTable на

select * from (select * from aGiantTable where ID in (select ID from #smallTable)) as aGiantTable

Или просто смириться с этим и замаскировать параметры, но, тем не менее, это несоответствие вызывает у меня любопытство.


ТЛ; DNR

Это вопиюще длинный вопрос, поэтому вкратце:

Создается ли полный план выполнения при первом вызове хранимой процедуры или при ее выполнении? То есть, если хранимая процедура состоит из нескольких шагов, план выполнения для каждого шага создается при первом вызове процедуры, или он создается только после завершения выполнения предыдущих шагов (опять же, при первом вызове)?

Ответы [ 2 ]

2 голосов
/ 15 апреля 2010

Это анализ параметров, и если у вас нет SQL Server 2008 и OPTIMIZE FOR UNKNOWN, то лучше всего маскировать параметры локальными переменными (как вы уже нашли).

1 голос
/ 15 апреля 2010

Некоторые дополнительные статьи для просмотра:

http://blogs.msdn.com/queryoptteam/archive/2006/03/31/565991.aspx http://sqlblog.com/blogs/ben_nevarez/archive/2009/08/27/the-query-optimizer-and-parameter-sniffing.aspx

Обратите внимание, что вы также можете использовать опцию запроса "пересобирать" для обхода "перехвата параметров"

...