2008-10-23 4 views
3

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

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

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

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

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

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

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

+0

План выполнения должен вас беспокоить, поскольку он говорит вам, что именно делает исполняемый файл запроса. – 2008-10-23 13:15:59

+0

Сложность не беспокоит меня прямо, потому что у меня есть очень сложные запросы, которые выполняются быстро. Это не печально. – 2008-10-23 13:47:03

+0

См. Также: http://stackoverflow.com/questions/532468/ignoring-a-null-parameter-in-t-sql/532510#532510 – 2009-02-12 15:06:43

ответ

4

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

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

BTW - я бы, вероятно, переписать ту часть запроса в объединенном производной таблицы:

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 

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

5

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

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

Как это:

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

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

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

+0

Это, скорее всего, лучший подход, да, а не динамический SQL или использование ISNULL, поскольку ISNULL не считает строки нулевой длины нулевыми. Это особенно проблема в зависимости от того, как вы вызываете свою процедуру (из какого приложения и т. Д.). Кроме того, выполнение, вероятно, будет быстрее, используя структуру if. Просто обратите внимание: структура if выше должна также проверять длину datalength (@parameter)> 0, чтобы избежать проблем с нулевой длиной строки. – 2010-09-01 09:42:44

2

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

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

+0

Сервер Sql делает оценку короткого замыкания. – 2008-10-23 13:57:10

+0

Я вполне уверен, что это не было в какой-то момент в прошлом - по крайней мере для операторов «if», но я признаю, что я не тестировал это, так как, вероятно, SQL Server 7. – 2008-10-23 15:23:47

2

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

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

4

заявления СЛУЧАЙ ваш друг ...

Вместо:

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) 

Я предпочитаю использовать СЛУЧАЙ больше, потому что мой вариант l параметры могут использовать определенные значения вместо NULL ...

1

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


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 
Смежные вопросы