考虑以下再现示例(小提琴):
CREATE FUNCTION dbo.Repro (@myYear int)
RETURNS datetime
AS
BEGIN
IF @myYear <> 1990
BEGIN
RETURN NULL
END
DECLARE @firstOfYear datetime;
SET @firstOfYear = DATEFROMPARTS(@myYear, 1, 1);
IF DATEDIFF(day, @firstOfYear, @firstOfYear) <> 0
BEGIN
RETURN NULL
END
RETURN @firstOfYear
END
SELECT dbo.Repro(0);
如果输入是
1990
,则该函数应返回1990年1月的第一天,否则返回NULL
。是的,我知道DATEDIFF(day, @firstOfYear, @firstOfYear) <> 0
是无意义的操作。这是一个演示潜在错误而不是生产代码的方法。现在让我们在SQL Server 2017和SQL Server 2019上执行
SELECT dbo.Repro(0)
。 :NULL
在SQL Server 2019上的实际结果:
消息289级16状态1行1
显然,SQL Server 2019执行初始防护子句(
NULL
)下的某些代码,即使它不应该执行。我的问题:
这是预期的行为,还是在SQL Server 2019中发现了错误?
如果这是预期的行为,如何正确编写保护输入参数的保护子句?
#1 楼
这是带有标量UDF内联的错误(或者可能是带有由标量UDF内联暴露的查询优化程序的错误)。您可以使用WITH INLINE = OFF
来关闭该函数的内联。使用变量而不是常量会显示更多细节
declare @myYear int = 0
SELECT dbo.Repro(@myYear);
节点5定义
Expr1000 = CASE WHEN [@myYear]<>(1990) THEN (1) ELSE (0) END
节点2定义
[Expr1003] = Scalar Operator(CONVERT_IMPLICIT(datetime,datefromparts([@myYear],(1),(1)),0))
使用文字
0
简化这些表达式分别为1
和CONVERT_IMPLICIT(datetime,datefromparts((0),(1),(1)),0)
。在评估时,
datefromparts(0
会引发错误。节点6定义了
Expr1002 = CASE WHEN [Expr1000] = (1) THEN (1) ELSE (0) END
Expr1002
用作嵌套循环连接(节点3)上的passthru谓词。在该嵌套循环的内部,常量扫描(节点7)不返回任何列。 passthru谓词移出到不受保护的区域。
评论
仅供参考Microsoft知道,并且下一个累积更新中应该有修复程序。感谢您举报,很抱歉您遇到了这个问题。
– Conor Cunningham MSFT
20/04/27在14:31
@ConorCunninghamMSFT:太好了,谢谢您的反馈!
–亨氏
20年4月28日在8:53