为什么此查询在存储过程中返回不同的结果?

时间:2010-12-21 18:04:39

标签: sql-server stored-procedures

概要

我有一个带有WHERE子句的查询,该子句包含一个条件,该条件针对可能为NULL的参数检查NULLable列,如下所示:

SELECT ...
FROM Table
WHERE NullableColumn = @NullableParameter

根据我的理解,SQL Server应该始终将NULL = NULL non-NULL = NULL评估为false,这意味着如果@NullableParameter为NULL,则上述查询将返回零记录。

这是我在查询窗口中运行查询时看到的行为,但是当我在存储过程中使用相同的查询并且@NullableParameter为NULL时,它返回NullableColumn为NULL的所有记录。简而言之,似乎sproc正在说NULL = NULL是真的。

这就是问题/问题。实际的WHERE子句涉及更多,并在下面的部分中提供了更多详细信息。

Gritty详细信息

我有一个名为 StudyResults 的数据库。每条记录由StudyResultId主键字段唯一标识。还有ParticipantId字段,用于指示研究中涉及的主题,以及GroupId,用于标识主题所属的组(如果有)。如果该研究是单人研究,GroupId为NULL。 ParticipantId不能为NULL。

我有一个存储过程,需要更新 StudyResults 表中某些特定研究的记录,但这里有一点 - 如果研究是单人研究,那么我需要更新一排;如果是小组研究,我想更新该组的 StudyResults 中的所有行。

这不是很难完成。存储过程传递StudyResultId,然后运行以下T-SQL以确定该行的GroupIdParticipantId值:

DECLARE @GroupId INT, @ParticipantId INT
SELECT @GroupId = GroupId,
       @ParticipantId = ParticipantId
FROM StudyResults
WHERE StudyResult = @StudyResultId

接下来,我创建一个CURSOR来枚举感兴趣的 StudyResults 记录。请注意WHERE子句,其中说明了“给我记录StudyResultId等于@StudyResultId传入sproc GroupId和{的记录{1}}值与感兴趣的 StudyResults 记录的ParticipantIdGroupId值对齐。

ParticipantId

如果DECLARE resultsToEnumerate CURSOR LOCAL FAST_FORWARD FOR SELECT DISTINCT StudyResultId FROM StudyResults WHERE StudyResult = @StudyResultId OR (GroupId = @GroupId AND ParticipantId= @ParticipantId) 为NULL,那么比较@GroupId 总是是假的,对吧?因为对于SQL Server,GroupId = @GroupId为false,NULL = NULL为false。

但是这里有些奇怪 - 如果我从查询窗口运行上述语句并使用non-NULL = NULL进行单人研究,则CURSOR包含我所期望的 - 即单个记录。但是,如果我将完全相同的代码放在存储过程中并运行它,则CURSOR包含该参与者的所有单人研究!好像它说@StudyResultId是NULL,所以我将返回@GroupId为NULL的所有记录,但为什么呢? GroupId永远不应该返回记录,对吗?

事实上,如果我进入sproc并更改NULL = NULL子句并将WHERE替换为GroupID = @GroupID,我会看到相同的结果 - 所有参与者的单人研究。所以它显然是在sproc中评估NULL = NULL为真(或忽略它)。

解决方法

我可以在存储过程中通过在括号中添加额外的检查来“修复”这一点,以确保NULL = NULL如此:

GroupId IS NOT NULL

这就是我所做的,但我很困惑为什么在我的存储过程中对WHERE ActivityID = @ActivityID OR (GroupID IS NOT NULL AND GroupID = @GroupID AND PatientID = @PatientID) 子句的评估方式不同。

2 个答案:

答案 0 :(得分:12)

在将ANSI_NULLS设置为ON后,尝试在会话中更改存储过程。

SET ANSI_NULLS ON
GO

alter procedure procedureName 
...

来自the docs

  

对于存储过程,SQL Server使用   来自的SET ANSI_NULLS设置值   最初的创作时间   存储过程。每当存储   程序随后执行,   SET ANSI_NULLS的设置是   恢复到原来使用的值   并生效。在里面调用时   存储过程,SET的设置   ANSI_NULLS未更改。

答案 1 :(得分:0)

添加此行代码

>   WITH RECOMPILE

在该过程的开始处,可以确保建立新的执行计划,而不依赖于先前的执行。