Есть ли способ сделать нулевую проверку переменной в предложении WHERE только один раз?


12

У меня есть запрос к большой таблице, которая выглядит так:

declare @myIdParam int = 1

select * 
from myTable
where (@myIdParam is null or myTable.Id = @myIdParam)

В предложении where есть несколько подобных условных выражений, подобных этому, и есть также много объединений, но это краткое изложение.

Фактически, если @myIdParam имеет значение null, мы не хотим ограничивать результаты с помощью этого параметра.

Я не специалист по БД, но из моих тестов кажется, что эта проверка NULL выполняется для каждой записи и никак не оптимизирована.

Если я уберу проверку «ноль» и предположу, что параметр не равен нулю, запрос мгновенно возвращается. В противном случае это займет до десяти секунд.

Есть ли способ оптимизировать это, чтобы проверка выполнялась только один раз во время выполнения?


1
Посмотрите на этот ответ: stackoverflow.com/questions/3415582/… tl; dr useOPTION(RECOMPILE)
vercelli

@vercelli это делает трюк. Учитывая, что этот вопрос действительно о дополнительных параметрах, я бы сказал, что это дубликат того, который вы связали.
Мистагог

Возможно, но это пост 6 лет назад. Может быть, с SqlServer 2014 или 2016 есть новый подход. (Я проверил его в 2014 году без перекомпиляции и взял навсегда)
vercelli

Поскольку ваш фактический запрос имеет много необязательных параметров, динамический SQL обеспечит наилучшую производительность. См. Sommarskog.se/dyn-search.html для подробной статьи по этому вопросу.
Дан Гузман

@DanGuzman, используя WITH RECOMPILE, как обрисовано в общих чертах в вопросе, связанном с vercelli, сократил время запроса с чуть менее минуты до практически мгновенного с очень избирательными критериями. Я считаю, что это лучший вариант для баланса производительности и читабельности.
Мистагог

Ответы:


8

Одним из способов является использование динамического SQL с использованием нулевой проверки для необязательного добавления этой части предложения where.

declare @myIdParam int = 1
declare @vc_dynamicsql varchar(max)

set @vc_dynamicsql = 'select * from myTable where 1=1'

if @myIdParam is not null
    set @vc_dynamicsql = @vc_dynamicsql + ' and  myTable.Id = @myIdParam'

EXECUTE sp_executesql @vc_dynamicsql

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

1
Это лучший способ обработать этот класс поисковых запросов. StackOverflow ответ ссылается @vercelli содержит большие ссылки для того, как сделать это.
Макс Вернон

Это лучший метод, но я заметил, что параметр @params для sp_ExecuteSQLотсутствует, и @vc_dynamicsqlпараметр должен быть a NVARCHAR.
Джеймс Андерсон

4

Каждый раз, когда вы помещаете функцию вокруг столбца ISNULL (@var, table.col), например, вы удаляете возможность SQL использовать индекс. Это действительно лучший вариант, если вы хотите сохранить его в одном запросе.

@var IS NULL or @var = table.col

В противном случае у вас есть два варианта. Первый - это динамический SQL, и для этого достаточно ответа @ Mystagogue, в противном случае вы можете задать два запроса, например:

IF @var is NULL
     SELECT * FROM table
ELSE
     SELECT * FROM table WHERE @var = col

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


Sql в вопросе не использует ISNULL или любую другую функцию.
Мистагог

@MystagogueI Я ссылался на удаленный ответ.
Кеннет Фишер

0

Ну, вы можете:

declare @myIdParam int = 1;

select *
from myTable
where nullif(@myIdParam, myTable.Id) is null;

Имейте в виду, однако, что nullif()функция по сути является оберткой case. Это не серебряная пуля, которая волшебным образом устраняет ORи, следовательно, ускоряет запрос.


использование функций в предложении where отрицательно влияет на производительность, потому что не позволяет использовать индексы (или я так слышал)
Mystagogue

@Mystagogue, да - обычно это делает условия поиска не SARGable. Увы, это единственный способ, которым я знаю, как ответить на ваш вопрос, не прибегая к динамическому SQL или множественным UNIONs. Когда у меня была именно эта задача, я выбрал динамический SQL.
Роджер Вольф
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.