【问题标题】:Working with optional stored procedure parameters使用可选的存储过程参数
【发布时间】:2008-10-23 13:03:55
【问题描述】:

我正在处理一个带有几个可选参数的存储过程。其中一些参数是单个值,很容易使用 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 是更好解决方案的实例之一吗?

【问题讨论】:

  • 执行计划应该会困扰您,因为它可以准确地告诉您查询执行器实际在做什么。
  • 复杂性并没有完全困扰我,因为我有一些非常复杂的查询可以快速运行。可悲的是,这个没有。

标签: sql database


【解决方案1】:

我会为参数是否可用创建单独的查询。

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

像这样:

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

如果你有很多参数要像这样重新设计,而你选择动态 sql 解决方案,那么也总是使用参数,你可能会被 SQL-Injection 错误咬伤。

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

【讨论】:

  • 这可能是最好的方法,是的,而不是动态 SQL 或使用 ISNULL 函数,因为 ISNULL 不会将零长度字符串视为 null。这尤其是一个问题,具体取决于如何调用您的过程(从哪个应用程序等)。此外,如果使用 if 结构,执行速度可能会更快。请注意:上面的 if 结构还应该测试 datalength(@parameter) > 0,以避免零长度字符串问题。
【解决方案2】:

主要问题可能是parameter sniffing,并且根据您的哪些参数为 NULL,最优执行计划大相径庭。尝试使用RECOMPILE 运行存储过程。

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

顺便说一句 - 我可能会将查询的那部分重写为 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

它可能会或可能不会影响查询计划,但它对我来说更清晰。

【讨论】:

    【解决方案3】:

    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...

    【讨论】:

    • 我强烈反对这种方法。如果您想在参数为空时提取所有结果,但您的字段可以为空,那么您将遇到“WHERE NULL = NULL”的情况,并且这些情况总是评估为 false,因此您将过滤掉字段值的结果包含空值。咬紧牙关,使用参数化的动态 SQL。
    • @MikeTeeVee,说得好。不过,我们的过滤器不会用于可为空的字段,因此我们不必担心。
    【解决方案4】:

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

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

    【讨论】:

    • Sql Server 确实会进行短路评估。
    • 我很确定它在过去的某个时候没有——至少对于“if”语句来说——但我承认我可能没有测试过这个,因为可能是 SQL Server 7 .
    【解决方案5】:

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

    但是,如果有多个参数,这将变得笨拙,因为您需要处理所有排列 - 5 个参数即 32 个静态查询!使用动态 SQL,您可以构建最适合给定参数的精确查询。不过一定要使用绑定变量!

    【讨论】:

    • 我只是将这个添加到我的答案中+1
    【解决方案6】:

    恕我直言,参数嗅探问题可以通过将所有参数复制到变量中来解决;然后不惜一切代价避免直接使用参数,而是使用变量。示例:

    
    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
    

    【讨论】:

    • 是的,我的 WHERE 子句肯定可以简化为:col1 = isnull(@col1, col1) and col2 = isnull(@col2, col2) and col3 = isnull(@col3, col3)跨度>
    • 不,最安全的方法是:(@col1 为 null 或 col1 = @col1) and (@col2 is null 或 col2 = @col2) and (@col3 is null 或 col3 = @col3)来自 3415582/how-can-i-use-optional-parameters-in-a-t-sql-stored-procedure。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-13
    • 2018-11-08
    • 2010-09-10
    • 1970-01-01
    • 2023-03-26
    • 1970-01-01
    相关资源
    最近更新 更多