2008-10-23 99 views
3

我正在使用多个可选参数的存储过程。其中一些参数是单值,它是很容易使用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会是更好的解决方案吗?

+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运行存储的proc。

与一些信念相反,Sql Server doesdo短路评估 - 尽管(与所有查询优化一样),它可能并不是您想要的。

顺便说一句 - 我可能会重写查询,作为加入派生表的那部分:

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不会将零长度字符串视为null。这取决于你的程序是如何被调用的(从什么应用程序等),这是一个特别的问题。此外,执行可能会更快,使用if结构。 只需注意:上面的if结构也应该测试数据长度(@参数)> 0,以避免零长度的字符串问题。 – 2010-09-01 09:42:44

2

动态SQL可能是在这种情况下,一个更好的解决方案,特别是如果存储过程只包装这个查询。

有一点要记住的是,SQL Server不会做布尔表达式的短路单个查询中。在许多语言中,“(a)||(b)”不会导致b被评估,如果a是真的。同样,“(a)& &(b)”如果a为假,则不会导致b被评估。在SQL Server中,情况并非如此。因此,在您给出的示例中,即使@NewGroupId不为空,“or”后端上的查询也会得到评估。

+0

Sql Server确实做短路评估。 – 2008-10-23 13:57:10

+0

我很确定它在过去的某个时间点没有 - 至少对于“if”语句 - 但我承认我没有测试过,因为可能是SQL Server 7. – 2008-10-23 15:23:47

2

对于少数可选参数,有条件的选择从一个几个静态查询作为GVS提示的是OK。

然而,这变得很困难,如果有一个几个参数,因为你需要处理所有排列 - 5个参数是32个静态查询!使用动态SQL,您可以构建最符合给定参数的精确查询。一定要使用绑定变量!

4

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陈述更多,因为我的选择l参数可能使用某些值而不是NULL ...

1

恕我直言,参数嗅探问题可以通过将所有参数复制到变量中解决;那么请避免直接使用参数,而应该使用变量。例如:


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