Работа с необязательными параметрами хранимой процедуры - PullRequest
3 голосов
/ 23 октября 2008

Я работаю над хранимой процедурой с несколькими необязательными параметрами. Некоторые из этих параметров являются отдельными значениями, и достаточно просто использовать предложение WHERE, например:

WHERE (@parameter IS NULL OR column = @parameter)

Однако в некоторых случаях условие WHERE является более сложным:

WHERE (@NewGroupId IS NULL OR si.SiteId IN (SELECT gs.SiteId
FROM [UtilityWeb].[dbo].[GroupSites] AS gs
WHERE gs.GroupId = @NewGroupId))

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

Есть ли лучшая практика или шаблон, который другие нашли для работы с необязательными параметрами в своих хранимых процедурах?

Является ли это одним из тех случаев, когда динамический SQL был бы лучшим решением?

Ответы [ 6 ]

5 голосов
/ 23 октября 2008

Я бы создал отдельные запросы для параметра, который доступен или нет.

Это создаст более простой SQL, а оптимизатор сделает лучшую работу.

Как это:

if (@parameter IS NULL) then begin
   select * from foo
end
else begin
   select * from foo where value = @parameter
end

Если вам нужно изменить многие параметры, как это, и вы выбираете динамическое решение sql, а затем всегда используете параметры, вас может укусить ошибка SQL-Injection .

Также возможна комбинация. Скорее всего используется запрос / запросы, которые вы кодируете полностью, и получаете предварительно скомпилированный. Все остальные комбинации создаются динамически.

4 голосов
/ 23 октября 2008

CASE заявления ваш друг ...

Вместо:

if (@parameter IS NULL) then begin
   select * from foo
end
else begin
   select * from foo where value = @parameter
end

Вы можете использовать:

SELECT * FROM foo 
WHERE value = CASE WHEN @parameter IS NULL THEN value ELSE @parameter END

Или

SELECT * FROM foo 
WHERE value = ISNULL(@parameter,value)

Я склонен чаще использовать операторы CASE, потому что мои необязательные параметры могут использовать определенные значения вместо NULL ...

4 голосов
/ 23 октября 2008

Основная проблема, вероятно, заключается в перехвате параметров , а также в совершенно разных оптимальных планах выполнения в зависимости от того, какой из ваших параметров равен NULL. Попробуйте запустить сохраненный процесс с RECOMPILE .

Вопреки некоторым убеждениям, Sql Server делает делает оценки короткого замыкания - хотя (как и во всех оптимизациях запросов) это может быть не совсем то, что вы хотели .

Кстати, я бы, вероятно, переписал эту часть запроса как производную таблицу JOINed:

SELECT * 
FROM Table as si
JOIN (
  SELECT SiteId
  FROM [UtilityWeb].[dbo].[GroupSites]
  WHERE GroupId = ISNULL(@NewGroupId, GroupId)
  /* --Or, if all SiteIds aren't in GroupSites, or GroupSites is unusually large 
     --this might work better
  SELECT @newGroupId
  UNION ALL
  SELECT SiteId FROM [UtilityWeb].[dbo].[GroupSites]
  WHERE GroupId = @NewGroupId
  */
) as gs ON
  si.SiteId = gs.SiteId

Может влиять или не влиять на план запроса, но для меня это чище.

2 голосов
/ 23 октября 2008

Для небольшого числа необязательных параметров условный выбор из одного из нескольких статических запросов, как предлагает GvS, вполне допустим.

Однако, это становится громоздким, если есть несколько параметров, так как вам нужно обрабатывать все перестановки - с 5 параметрами, что составляет 32 статических запроса! Используя динамический SQL, вы можете создать точный запрос, который наилучшим образом соответствует заданным параметрам. Обязательно используйте переменные связывания, хотя!

2 голосов
/ 23 октября 2008

Динамический SQL, вероятно, является лучшим решением в этом случае, особенно если хранимая процедура содержит только один запрос.

Следует иметь в виду, что SQL Server не выполняет короткое замыкание логических выражений внутри одного запроса. Во многих языках "(a) || (b)" не приведет к тому, что b будет оцениваться, если a истинно. Аналогичным образом, «(a) && (b)» не приведет к тому, что b будет оцениваться, если a равно false. В SQL Server это не так. Таким образом, в приведенном вами примере запрос в конце «или» будет оценен, даже если @NewGroupId не равен нулю.

1 голос
/ 08 апреля 2009

ИМХО, проблема с анализом параметров может быть решена путем копирования всех параметров в переменные; затем избегайте использования параметров напрямую любой ценой, вместо этого используйте переменные. Пример:


create proc ManyParams
(
    @pcol1 int,
    @pcol2 int,
    @pcol3 int
)
as
declare
    @col1 int,
    @col2 int,
    @col3 int

select
    @col1 = @pcol1,
    @col2 = @pcol2,
    @col3 = @pcol3

select 
    col1,
    col2,
    col3
from 
    tbl 
where 
    1 = case when @col1 is null then 1 else case when col1 = @col1 then 1 else 0 end end
and 1 = case when @col2 is null then 1 else case when col2 = @col2 then 1 else 0 end end
and 1 = case when @col3 is null then 1 else case when col3 = @col3 then 1 else 0 end end
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...