Обычно, когда оптимизатор генерирует план для запроса, этот план должен быть действительным для любых возможных значений параметров.Обычно план кэшируется и не генерируется повторно при повторном выполнении того же запроса, поэтому он должен оставаться действительным (дать правильный результат), даже если вы повторно запустите запрос с другим значением параметра.
Таким образом, план для первого запроса должен иметь такую форму, которая будет работать для любого значения @mID
, включая NULL и not-NULL.
Index IX_NC_F_Media_StateNotDeleted
может использоваться для поиска значенийдля обеих частей выражения OR
, но либо оптимизатор недостаточно умен, чтобы построить план, который выполняет два поиска по этому индексу, а затем объединяет результат, либо оптимизатор решил, что такой план будет более дорогим.
Таким образом, либо оптимизатор не способен видеть здесь , что @mid IS NULL
является взаимоисключающим с ID = @mID
, либо он решает, что альтернатива будет более дорогой.
Второй запрос с явным IF
делает выбор оптимизатора очевидным.
Этот тип запроса называется «всеобъемлющим» или «получающим» запросом.Я рекомендую прочитать отличную статью Эрланда Соммарскога Условия динамического поиска в T-SQL .
Во многих случаях целесообразно добавить OPTION (RECOMPILE)
в ваш первый запрос, например так:
declare @mID int = 400000;
select State
from Table1
where (ID = @mID and State in (0, 1, 4, 5))
or (@mID IS NULL and State in (0, 4))
OPTION(RECOMPILE);
SET @mID = NULL;
select State
from Table1
where (ID = @mID and State in (0, 1, 4, 5))
or (@mID IS NULL and State in (0, 4))
OPTION(RECOMPILE);
Попробуйте выполнить эти запросы и проверить их фактические планы выполнения.Вы должны увидеть, что форма плана изменяется в соответствии с фактическим значением параметра во время выполнения.
С OPTION(RECOMPILE)
оптимизатор знает, что сгенерированный план не будет кэшироваться как обычно, поэтому он принимает фактическийзначения параметров и встраивает их как константы в запрос.Как только они являются константами, здесь , оптимизатор может видеть, что NULL IS NULL
всегда истинно (или 400000 IS NULL
всегда ложно) и сворачивает / упрощает логическое выражение.Кроме того, оптимизатор может выбрать наиболее подходящие индексы в каждом случае.
Хотя во втором случае выполняется поиск по сравнению со случаем, когда @mID равен нулю, действительно ли это так?есть какая-то разница?Всплывающая подсказка указывает на то, что производительность почти одинакова, и я предполагаю, что это связано с тем, что данные в основном состоят из строк = 0.
В этом случае поиск индекса практически одинаковкак сканирование всей таблицы.Их размеры (в страницах) одинаковы.Если в вашей таблице много строк с State=2
, то отфильтрованный индекс будет более эффективным, так как он будет содержать меньше страниц, чем основная таблица.