Своеобразный случай с SQL Server, индексами и параметрами - PullRequest
1 голос
/ 23 октября 2009

У меня есть таблица, назовем ее History. Первичный ключ (он же Clustered Index) называется HIST_ID. Таблица содержит около 2300 строк в базе данных разработки. Теперь рассмотрим следующие два запроса:

Запрос 1:

declare @x int
set @x = 14289

select * from History where hist_id=@x

Запрос 2:

declare @x int
set @x = 14289

select * from History where hist_id=@x or @x is null

Единственная разница - or @x is null в конце. Однако первый запрос выполняет поиск по индексу, второй - сканирование по индексу. Что дает?

Превентивный ответ - нет, опция (перекомпилировать) не помогает.

Добавлено: Мне бы хотелось несколько аргументированных фактов, а не догадок. Я могу угадать дюжину возможных причин для этого сам. Но в чем здесь проблема реальная ?

Ответы [ 3 ]

1 голос
/ 23 октября 2009

Я бы предположил, что план создается отдельно от параметра, который передается / используется, поэтому, по сути, существует требование (в зависимости от значения @x) возвращать каждую строку. Таким образом, план запроса имеет дело с худшим сценарием параметров, которые он может получить.

например. Если бы вход для @x был нулевым, то запрос был бы вынужден возвращать каждую строку, поскольку каждая строка удовлетворяла бы буквальному уравнению / предикату, который всегда возвращал истину. Чтобы план запроса охватывал каждое значение @x, он должен создать план, который выполняет сканирование.

0 голосов
/ 23 октября 2009

Конечно это сканирование индекса.

Сканирование кластеризованного индекса = сканирование таблицы, поскольку у вас нет разумного предиката для "@x IS NULL".

Параметризованный, кэшированный план имеет общий характер и будет работать для @x = NULL или @x = value. Если вы не определили @x, вы должны получить тот же план.

Если вы закодировали «12345 IS NULL», то это обнаруживается и игнорируется.

Я не могу найти в блоге статью о том, как константы обрабатываются в планах запросов. Суть в том, что они обобщены, и короткие замыкания не позволяют повторное использование плана.

0 голосов
/ 23 октября 2009

Я думаю, оптимизатор считает, что это выгодно. Альтернативой может быть использование того же плана, как если бы вы написали

select * from History where hist_id=@x
union all
select * from History where @x is null

Вы можете переписать запрос таким образом, но я уверен, что оптимизатор способен сделать это сам по себе. Сколько у вас нулевых значений?

Редактировать: Оказывается, я неправильно прочитал вопрос и подумал, что вы хотели ГДЕ (@x = hist_id ИЛИ hist_id равен нулю). На самом деле вы хотите динамический критерий. Проверьте эту статью . Обратите внимание, что этот тип запроса должен был работать в SQL2k8, если вы указали WITH (RECOMPILE), но из-за неприятной ошибки эта поддержка была удалена.

...