我有一个带有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
和{的记录{1}}值与感兴趣的 StudyResults 记录的ParticipantId
和GroupId
值对齐。
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)
子句的评估方式不同。
答案 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
在该过程的开始处,可以确保建立新的执行计划,而不依赖于先前的执行。