【问题标题】:Ignoring a NULL parameter in T-SQL忽略 T-SQL 中的 NULL 参数
【发布时间】:2010-10-06 15:46:35
【问题描述】:

我希望能够传入一个参数列表,而忽略那些为 NULL 的参数。这样查询实际上就假装过滤器不存在并忽略它。

我是这样做的:

(@thing IS NULL or Thing=@thing) 

这是对的,如果是的话,它的表现会很差吗?这似乎比单独构建 SQL 慢很多。

这样做的最佳方法是什么?

已修复!请参阅 Marc Gravell 的回答。总之,多次使用 IS NULL 会影响性能很大

【问题讨论】:

    标签: performance tsql parameters


    【解决方案1】:

    一旦您获得了其中的几个以上,那么是的:它开始变得非常缓慢。在这种情况下,我倾向于使用生成的 TSQL - 即

    DECLARE @sql nvarchar(4000)
    SET @sql = /* core query */
    
    IF @name IS NOT NULL
        SET @sql = @sql + ' AND foo.Name = @name'
    
    IF @dob IS NOT NULL
        SET @sql = @sql + ' AND foo.DOB = @dob'
    
    // etc
    
    EXEC sp_ExecuteSQL @sql, N'@name varchar(100), @dob datetime',
            @name, @dob
    

    请注意,sp_ExecuteSQL 缓存查询计划,因此任何具有相同参数的查询都可能重复使用该计划。

    缺点是除非您签署 SPROC,否则调用者需要对表具有 SELECT 权限(而不仅仅是 SPROC 上的 EXEC 权限)。

    【讨论】:

    • 对不起,作为菜鸟问题,你是什么意思签署存储区?当然我需要一个选择来定义表格?
    • 我该如何处理它可能包含额外的和
    • 表等将是 /* 核心查询 */ 位(您需要填写)的一部分。如果您还没有WHERE,您可以添加一个虚拟“WHERE 1 = 1”,或者您必须使用“STUFF(...)”之类的东西(或其他字符串操作)来更改第一个 AND 到 WHERE
    • (如果您有一个单独的过滤器变量,这是最简单的 - 例如,@where,并作为倒数第二步连接)
    • 重新签名:msdn.microsoft.com/en-us/library/bb669102.aspx - 即使没有对表的 SELECT 访问权限,用户也可以调用 SP(并让它工作) - 如果不签名,EXEC 将使用用户的权限。注意:我从来没有在现实生活中签署过什么……也许可以忽略它。
    【解决方案2】:

    我会这样处理。

    WHERE Thing = ISNULL(@Thing, Thing)
    

    如果您只是将参数用作 where 子句的过滤器,这将非常有效。如果为null,它将忽略该参数。

    【讨论】:

    • 我发现,像 OP 一样,如果使用其中的几个以上,它会很快降低性能,因为它无法轻松识别最有用的索引等。
    • 我同意 ISNULL 技巧可能是一个性能问题 - 我会将其展开为 OR 给优化器一个更好的机会。还要注意 ISNULL 采用第一个参数的数据类型,因此可能会产生奇怪的副作用。 COALESCE 可能是更好的选择。
    • 避免!不要使用此示例。如果您为“@Thing”传入 null 并想要提取所有值(包括 null),这将不起作用。如果“Thing”为空,你会得到 null = null,每个人都知道它的计算结果为 false。
    • @MikeTeeVee 是的,你是对的。如果这是您的情况,那么是的,您应该使用不同的方法。如果您不允许在列上使用空值,那么这种方法很好。 (如果你做了很多这样的事情,就像许多没有适度使用的东西一样,那么是的,你会受到性能影响)
    【解决方案3】:

    我一般用

    WHERE (id = @id OR @id IS NULL)
    AND (num = @num OR @num IS NULL)
    

    等等

    【讨论】:

      【解决方案4】:

      我过去在这种情况下使用的一种技术是使用 COALESCE 函数作为我的 WHERE 子句的一部分。 Books Online 将提供有关该功能的更深入信息,但这里有一个关于如何在您描述的场景中使用它的 sn-p:

      create procedure usp_TEST_COALESCE
      (
          @parm1 varchar(32) = null,
          @parm2 varchar(32) = null,
          @parm3 int = null
      )
      AS
      
      SELECT * 
      FROM [TableName]
      WHERE Field1 = COALESCE(@parm1, Field1)
      AND Field2 = COALESCE(@parm2, Field2)
      AND Field3 = COALESCE(@parm3, Field3)
      

      COALESCE 函数将从其参数中返回第一个非空表达式。在上面的示例中,如果任何参数为空,COALESCE 函数将使用基础字段中的值。

      使用此技术的一个重要警告是表中的基础字段(构成您的 where 子句)需要不可为空。

      【讨论】:

      • 对于我使用的大型数据集,我发现这种方法(虽然干净)比 (@parm2 IS NULL OR Field2 = @parm2) 慢
      • 嗨,如果 Field1 是 NULLable 值怎么办?那么写 WHERE COALESCE(Field1,'')=COALESCE(@parm1,Field1,'') 是个好习惯吗?
      【解决方案5】:

      我不确定这是否是“最佳”方式,但这正是我在存储过程中出于相同目的所做的。我的直觉是,从执行计划的角度来看,这比动态创建的查询要快。另一种选择是为您传入的这些“标志”的每个组合创建一个查询,但这确实不是可扩展的。

      【讨论】:

      • 每个执行计划 - sp_ExecuteSQL 之类的东西将缓存和重用查询计划;因此,重复使用的专业计划可以轻松胜过通用静态版本...
      • 我从来没有注意到可测量的差异,我总是将可读性提高几毫秒(除非有相反的 SLA 要求)。
      • FWIW 我已经赚了很多咨询费,只是帮助编码人员从巨大的 WHERE 子句转移到参数化的 sp_ExecuteSQL 结构,这是提高性能的最快方法。编码人员通常使用连接“位”的 WHERE 子句更改为 sp_ExecuteSQL 是答案
      【解决方案6】:

      在标题为“案例研究:搜索订单”的部分中查看以下 link。这将深入探讨所有选项,并应为您提供与每个选项相关的成本的出色概述。警告,使用 COALESCE 时要非常小心,它可能不会返回您认为的内容。

      问候,

      提姆

      【讨论】:

        【解决方案7】:

        这是我通常使用的方法。我认为它没有效率低下的原因,因为如果@thing 为空,该语句应该短路为真,因此不需要表扫描。您是否有任何证据表明这种比较会减慢您的查询速度?如果没有,我不会担心。

        【讨论】:

        • 是的,10 年后,至少在 MS SQL Server 中仍然如此。像你一样,我错误地假设“如果@thing 为空,则语句应该短路为真,因此不需要表扫描。”...结果 each 请求 400K 行,包含 15 个参数需要 10 多秒!
        【解决方案8】:

        当您声明参数时,如果您为它们设置了一个值,例如 null 在您的情况下,您不需要将值传递给它们,除非您当然需要。我使用此功能来标记是否需要运行另一个查询是参数不为空的特殊情况

        我通常只是这样检查它

        如果字段为空

        【讨论】:

          【解决方案9】:

          谢谢,这很有帮助。由于提到的潜在性能优势,我决定使用 sp_ExecuteSQL 方法。我对此略有不同的看法,可能会对您有所帮助。

          DECLARE @sql nvarchar(4000) 
          DECLARE @where nvarchar(1000) =''
          
          SET @sql = 'SELECT * FROM MyTable'
          
          IF @Param1 IS NOT NULL 
              SET @where = @where + ' AND Field1 = @Param1'
          
          IF @Param2 IS NOT NULL 
              SET @where = @where + ' AND Field2 = @Param2' 
          
          IF @Param3 IS NOT NULL 
              SET @where = @where + ' AND Field3 = @Param3' 
          
          -- Add WHERE if where clause exists, 1=1 is included because @where begins with AND
          IF @where <> ''
              SET @sql = @sql + ' WHERE 1=1' + @where
          
          --Note that we could also create order parameters and append here
          SET @sql = @sql + ' ORDER BY Field1'
          

          【讨论】:

            【解决方案10】:

            如果 Thing(列值)也是 Nullable,则使用 foll。方法:

            WHERE COALESCE(Thing,'')=COALESCE(@thing,Thing,'')
            

            【讨论】:

              猜你喜欢
              • 2015-10-05
              • 2011-01-23
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2016-08-10
              • 2015-02-24
              • 1970-01-01
              相关资源
              最近更新 更多