为什么此查询在存储过程中返回不同的结果?
概要
我有一个带有
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
条款涉及更多,并在下面的部分中提供了更多细节。
坚韧不拔的细节
我有一个名为StudyResults的数据库。每条记录由StudyResultId
主键字段唯一标识。还有一个ParticipantId
字段,表示参与研究的主题,和GroupId
,它标识主题属于哪个组,如果有的话。如果该研究是单人研究,则GroupId
为NULL。 ParticipantId
不能为NULL。
我有一个存储过程,需要更新StudyResults表中某些特定研究的记录,但这里有一点 - 如果研究是单人研究,那么我需要更新那一行;如果是小组研究,我想更新该组的StudyResults中的所有行。
这不是很难完成。传递存储过程StudyResultId
,然后运行以下T-SQL以确定该行的GroupId
和ParticipantId
值:
DECLARE @GroupId INT, @ParticipantId INT
SELECT @GroupId = GroupId,
@ParticipantId = ParticipantId
FROM StudyResults
WHERE StudyResult = @StudyResultId
接下来,我创建一个CURSOR来枚举感兴趣的StudyResults记录。注意WHERE
条款,其中说:“给我记录,其中StudyResultId
等于@StudyResultId
进入sproc或GroupId
和ParticipantId
值与GroupId
和ParticipantId
值对应的StudyResults记录。
DECLARE resultsToEnumerate CURSOR LOCAL FAST_FORWARD FOR
SELECT DISTINCT StudyResultId
FROM StudyResults
WHERE StudyResult = @StudyResultId OR (GroupId = @GroupId AND ParticipantId= @ParticipantId)
如果@GroupId
为NULL,那么比较GroupId = @GroupId
应该总是假的,对吧?因为对于SQL Server,NULL = NULL
为false,而non-NULL = NULL
为false。
但是这里有些奇怪 - 如果我从查询窗口运行上述语句并使用@StudyResultId
进行单人研究,则CURSOR包含我所期望的 - 即单个记录。但是,如果我将完全相同的代码放在存储过程中并运行它,则CURSOR包含该参与者的所有单人研究!就好像它说@GroupId
是NULL,所以我会返回所有记录,其中GroupId
是NULL,但为什么呢? NULL = NULL
永远不应该返回记录,对吧?
事实上,如果我进入sproc并更改WHERE
条款并将GroupID = @GroupID
替换为NULL = NULL
,我会看到相同的结果 - 所有参与者的单人研究。所以它显然是在sproc中评估NULL = NULL
为真(或忽略它)。
解决方法
我可以通过在括号中添加一个额外的检查来“修复”这个存储过程,以确保GroupId IS NOT NULL
像这样:
WHERE ActivityID = @ActivityID OR (GroupID IS NOT NULL AND GroupID = @GroupID AND PatientID = @PatientID)
这就是我所做的,但我很困惑为什么在我的存储过程中对WHERE
子句进行不同的评估。
没有找到相关结果
已邀请:
2 个回复
春驹晴陪
来自文档: 对于存储过程,SQL Server使用 来自的SET ANSI_NULLS设置值 最初的创作时间 存储过程。每当存储 程序随后执行, SET ANSI_NULLS的设置是 恢复到原来使用的值 并生效。在里面调用时 存储过程,SET的设置 ANSI_NULLS未更改。
雇砰
在程序开始时,它确保构建一个新的执行计划,而不是依赖于先前的执行。