【问题标题】:T-SQL Stored Procedure NULL input values cause select statement to failT-SQL 存储过程 NULL 输入值导致 select 语句失败
【发布时间】:2008-12-19 12:53:27
【问题描述】:

下面是一个存储过程,用于根据单独检查所有字段来检查数据库中是否存在重复条目(不要问我为什么要这样做,它只是必须这样)。

这听起来很简单,但是 SP 失败了。 问题是传递给 SP 的某些参数可能具有 null 值,因此 sql 应该读取“is null”而不是“= null”。 我用 exec() 和 sp_executesql 尝试了 isnull()、case 语句、coalesce() 和动态 sql,但未能实现其中任何一个。这是代码...

CREATE PROCEDURE sp_myDuplicateCheck
 @userId int,
 @noteType char(1),
 @aCode char(3),
 @bCode char(3), 
 @cCode char(3),
 @outDuplicateFound int OUT
AS
BEGIN
SET @outDuplicateFound = (SELECT Top 1 id FROM codeTable 
                          WHERE userId = @userId
                          AND noteType = @noteType
                          AND aCode = @aCode
                          AND bCode = @bCode
                          AND cCode = @cCode 
                          )
-- Now set the duplicate output flag to a 1 or a 0
IF (@outDuplicateFound IS NULL) OR (@outDuplicateFound = '') OR (@outDuplicateFound = 0)
 SET @outDuplicateFound = 0
ELSE 
 SET @outDuplicateFound = 1
END

【问题讨论】:

    标签: sql tsql stored-procedures parameters null


    【解决方案1】:

    我认为每个可能为空的参数都需要这样的东西:

    AND (aCode = @aCode OR (aCode IS NULL AND @aCode IS NULL))
    

    【讨论】:

    • 非常感谢您的帮助 Mehrdad。现在看起来很明显,但它真的让我很难过。
    • 我喜欢 ISNULL(@aCode, aCode) = aCode。
    • 是的,这和用 IsNull 包裹两者是一回事,它更短且更易于维护。
    • 但这行不通 - NULL != NULL。你所做的就是忽略参数,如果它是 NULL 并且这不是 OP 想要的。
    【解决方案2】:

    如果我正确理解你的问题,那么我鼓励你做一些研究:

    SET ANSI_NULLS OFF
    

    如果您在存储过程中使用此命令,则可以在比较中使用 = NULL。看看下面的示例代码,看看它是如何工作的。

    Declare @Temp Table(Data Int)
    
    Insert Into @Temp Values(1)
    Insert Into @Temp Values(NULL)
    
    -- No rows from the following query
    select * From @Temp Where Data = NULL
    
    SET ANSI_NULLS OFF
    
    -- This returns the rows where data is null
    select * From @Temp Where Data = NULL
    
    SET ANSI_NULLS ON
    

    当您将 ANSI_NULLS 设置为 Off 时,最好尽快将其设置回 ON,因为这可能会影响您稍后运行的其他查询。所有 SET 命令仅影响当前会话,但根据您的应用程序,这可能跨越多个查询,这就是为什么我建议您在此查询之后立即重新打开 ansi nulls。

    【讨论】:

      【解决方案3】:

      我认为这应该与 COALESCE 功能一起使用。试试这个:

      CREATE PROCEDURE sp_myDuplicateCheck
       @userId int,
       @noteType char(1),
       @aCode char(3),
       @bCode char(3), 
       @cCode char(3),
       @outDuplicateFound int OUT
      AS
      BEGIN
      
      SET @outDuplicateFound = (SELECT Top 1 id FROM codeTable 
                                WHERE userId = @userId
                                AND noteType = @noteType
                                AND COALESCE(aCode,'NUL') = COALESCE(@aCode,'NUL')
                                AND COALESCE(bCode,'NUL') = COALESCE(@bCode,'NUL')
                                AND COALESCE(cCode,'NUL') = COALESCE(@cCode,'NUL')
                                )
      -- Now set the duplicate output flag to a 1 or a 0
      IF (@outDuplicateFound IS NULL) OR (@outDuplicateFound = '') OR (@outDuplicateFound = 0)
       SET @outDuplicateFound = 0
      ELSE 
       SET @outDuplicateFound = 1
      END
      

      祝你好运!

      杰森

      【讨论】:

      • 无论您的输入值是否为 NULL,或者您的列是否包含 NULL,这都应该有效。
      【解决方案4】:

      试试这个:

      CREATE PROCEDURE sp_myDuplicateCheck
           @userId int = 0,
           @noteType char(1) = "",
           @aCode char(3) = "", 
           @bCode char(3) = "", 
           @cCode char(3) = "",
           @outDuplicateFound int OUT
          AS
          BEGIN
          SET @outDuplicateFound = (SELECT Top 1 id FROM codeTable 
                                    WHERE @userId in (userId ,0)
                                    AND @noteType in (noteType,"")
                                    AND @aCode in (aCode , "")
                                    AND @bCode in (bCode , "")
                                    AND @cCode in (cCode ,"")
                                    )
          -- Now set the duplicate output flag to a 1 or a 0
          IF (@outDuplicateFound IS NULL) OR (@outDuplicateFound = '') OR (@outDuplicateFound = 0)
           SET @outDuplicateFound = 0
          ELSE 
           SET @outDuplicateFound = 1
          END
      

      这基本上是在 null 的情况下为输入参数提供默认值,然后在 where 条件中检查 如果值不等于默认值。

      【讨论】:

      • 赞成,这对我来说是最有用的。这使我可以在不需要时完全跳过传递参数。 C# null != SQL null。
      【解决方案5】:

      我将首先添加一个检查以查看所有参数在运行时是否为空,即,

      IF(COALESCE(@userId, @noteType, @aCode, @bCode, @cCode) IS NULL)
         BEGIN
             -- do something here, log, print, return, etc.
         END
      

      然后,在您验证用户传递了某些内容后,您可以在 WHERE 子句中使用类似的内容

      WHERE userId = COALESCE(@userId, userId)
      AND noteType = COALESCE(@noteType, noteType)
      AND aCode    = COALESCE(@aCode, aCode)
      AND bCode    = COALESCE(@bCode, bCode)
      AND cCode    = COALESCE(@cCode, cCode)
      

      编辑:我可能错过了如果参数作为 null 传入的意图,这意味着您明确想要测试该列是否为 null。我上面的 where 子句假设 null 参数意味着“跳过此列的测试”。

      另外,我相信您可以使用原始查询并在存储过程创建时添加 ANSI_NULLS 设置选项。例如,

      SET ANSI_NULLS OFF
      GO
      CREATE PROC sp_myDuplicateCheck....
      

      实际上这应该允许您的代码评估 column=null 而不是 column is null。我认为 Kalen Delaney 曾经将 ANSI_NULLS 和 QUOTED_IDENTIFIER 选项创造为“粘性选项”,因为如果它们是在过程创建时设置的,它们会在运行时与过程保持一致,无论当时的连接如何时间已定。

      【讨论】:

        【解决方案6】:

        SET ANSI_NULLS OFF/On

        这样你就可以做到colName = null

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2011-03-16
          • 2013-01-05
          • 1970-01-01
          • 2019-01-14
          • 2023-01-12
          • 1970-01-01
          • 2019-07-16
          • 2010-12-24
          相关资源
          最近更新 更多