TSQL如果似乎从错误的逻辑分支中随机执行代码
我有一个IDEMPOTENT脚本,该脚本将不同的数据库部署带到最新的修订版中。代码中引用的列实际上将在以后的版本中删除,因此为了保留脚本的有效性,我在执行语句之前检查列是否存在。我注意到一些意外的行为,即IF逻辑分支在条件是错误的情况下似乎间歇性地执行。
基本示例
IF (1 = 0)
BEGIN
PRINT 'Adding data'; -- Never printed
END
GO
测试逻辑分支
IF (1 = 0)
BEGIN
PRINT 'Adding data'; -- Never printed
RAISERROR('One never equals zero', 16, 1); -- Never raised
END
GO
添加数据修改代码
IF (1 = 0)
BEGIN
PRINT 'Adding data'; -- Never printed
RAISERROR('One never equals zero', 16, 1); -- Never raised
INSERT INTO [tbl_Users] ( NonExistentColumn ) VALUES ( 'Bar' ); -- Msg 207, Level 16, State 1, Line 6; Invalid column name 'NonExistentColumn'.
END
GO
为什么会发生这种情况?特别是,为什么print
和raiserror
行正确忽略了,但是即使应该忽略整个逻辑分支,insert
仍执行仍执行?
I have an idempotent script that brings different database deployments up to the latest revision. The column referenced in the code is actually deleted in a future release, so in order to preserve the script's validity I check if the column exists before executing the statement. I noticed some unexpected behaviour whereby the IF logic branches seem to intermittently execute when the condition is false.
Basic example
IF (1 = 0)
BEGIN
PRINT 'Adding data'; -- Never printed
END
GO
Testing the logic branch
IF (1 = 0)
BEGIN
PRINT 'Adding data'; -- Never printed
RAISERROR('One never equals zero', 16, 1); -- Never raised
END
GO
Added data modification code
IF (1 = 0)
BEGIN
PRINT 'Adding data'; -- Never printed
RAISERROR('One never equals zero', 16, 1); -- Never raised
INSERT INTO [tbl_Users] ( NonExistentColumn ) VALUES ( 'Bar' ); -- Msg 207, Level 16, State 1, Line 6; Invalid column name 'NonExistentColumn'.
END
GO
Why does this happen? Particularly, why are the PRINT
and RAISERROR
lines correctly ignored but the INSERT
still executes, even though the entire logic branch should be ignored?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
您的陈述均未运行,它不会跳过
print
和raiserror
,然后转到插入
,而是整个批处理失败的到期汇编错误。尽管有一些信念,但t-sql是 并在执行之前进行验证的,如果该语句没有有效,则整个批次将无法运行;这就是为什么当您有语法错误时,没有任何语句运行。
在这里,您有一个无效的列参考,列
nonexistentColumn
在表[yourdefaultschema] .tbl_users
不存在。尽管SQL Server确实具有一些延迟的对象验证,但针对存在的对象不是其中之一。因此,当您要求执行批次时,解析器检查以查看表[yourDefaultSchema] .tbl_users
具有一个列nonexistentColumn
,当它发现它不'' t,它提出了一个“无效列名”错误。如果表本身不存在,则不会发生此错误。例如,采取以下批次:
在SSMS的消息窗格中,您将获得以下内容:
请注意,第一个批次打印中没有一个
print
语句,因为批次未运行,但是在第二批次中,由于列的验证将推迟到该语句运行(并且永远不会运行,因此不会发生错误)。如果要在现有对象上引用潜在的不存在的列,请使用
sys.sp_executesql
>:None of your statements are run, it isn't skipping the
PRINT
andRAISERROR
and going to theINSERT INTO
, instead the entire batch fails due to a compilation error.Despite some beliefs, T-SQL is parsed and validated before it is executed, and if the statement isn't valid then the entire batch won't be run; this is why when you have a syntax error, for example, none of the statements are run.
Here you have an invalid column reference, the column
NonExistentColumn
in the table[YourDefaultSchema].tbl_Users
doesn't exist. Although SQL Server does have some deferred object validation, columns against objects that exist are not one of them. As such, when you ask for your batch to be executed, the parser checks to see table[YourDefaultSchema].tbl_Users
has a columnNonExistentColumn
, and when it finds it doesn't, it raises an "Invalid Column Name" error.If the table itself did not exist, then this error would not occur. For example, take the following batches:
In the messages pane of SSMS you will get the following:
Notice that none of the
PRINT
statements from the first batch print, as the batch isn't run, however, in the second batch they are, as the validation of the column is deferred till that statement would be run (and it's never run, so no error occurs).If you wanted to reference a potentially non-existent column on an existing object, defer the statement's validation using
sys.sp_executesql
:This simply prints the following:
db<>fiddle