使用可选的存储过程参数

时间:2008-10-23 13:03:55

标签: sql database

我正在处理带有几个可选参数的存储过程。其中一些参数是单个值,并且很容易使用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 个答案:

答案 0 :(得分:5)

我会为可用或不可用的参数创建单独的查询。

这将创建更简单的SQL,优化器将做得更好。

像这样:

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

在你需要重新设计许多参数时,你需要动态的sql解决方案,然后也总是使用参数,你可能会被SQL-Injection错误所困扰。

组合也是可能的。最可能使用的查询/查询,您可以完整编码,并获得预编译。所有其他组合都是动态创建的。

答案 1 :(得分:4)

主要问题可能是parameter sniffing,并且根据您的哪些参数为NULL,优化执行计划截然不同。尝试使用RECOMPILE运行存储过程。

与某些观点相反,Sql Server does do短路评估 - 尽管(与所有查询优化一样)可能不是您想要的。

BTW - 我可能会将查询的那部分重写为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 :(得分: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语句,因为我的可选参数可能使用某些值而不是NULL ...

答案 3 :(得分:2)

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

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

答案 4 :(得分:2)

对于少量可选参数,从GvS建议的几个静态查询中选择一个条件是可以的。

但是,如果存在多个参数,则会变得难以处理,因为您需要处理所有排列 - 使用5个参数即32个静态查询!使用动态SQL,您可以构造最适合给定参数的精确查询。一定要使用绑定变量!

答案 5 :(得分: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