TSQL如果似乎从错误的逻辑分支中随机执行代码

发布于 2025-02-13 21:42:39 字数 984 浏览 0 评论 0原文

我有一个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

为什么会发生这种情况?特别是,为什么printraiserror行正确忽略了,但是即使应该忽略整个逻辑分支,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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

伪装你 2025-02-20 21:42:39

您的陈述均未运行,它不会跳过printraiserror,然后转到插入,而是整个批处理失败的到期汇编错误。

尽管有一些信念,但t-sql是 并在执行之前进行验证的,如果该语句没有有效,则整个批次将无法运行;这就是为什么当您有语法错误时,没有任何语句运行。

在这里,您有一个无效的列参考,列nonexistentColumn在表[yourdefaultschema] .tbl_users不存在。尽管SQL Server确实具有一些延迟的对象验证,但针对存在的对象不是其中之一。因此,当您要求执行批次时,解析器检查以查看表[yourDefaultSchema] .tbl_users具有一个列nonexistentColumn,当它发现它不'' t,它提出了一个“无效列名”错误。

如果表本身不存在,则不会发生此错误。例如,采取以下批次:

CREATE TABLE dbo.SomeTable (SomeColumn int);
GO
PRINT N'First Batch Starts';    
IF 1 = 0 BEGIN
    PRINT N'Entered first IF.';
    INSERT INTO dbo.SomeTable (AnotherColumn)
    VALUES(1);
END;
PRINT N'First Batch Ends';    
GO
PRINT N'Second Batch Starts';    
IF 1 = 0 BEGIN
    PRINT N'Entered second IF.';
    INSERT INTO dbo.AnotherTable (AnotherColumn)
    VALUES(1);
END;
PRINT N'Second Batch Ends';
GO

DROP TABLE dbo.SomeTable;

在SSMS的消息窗格中,您将获得以下内容:

Msg 207, Level 16, State 1, Line 6
Invalid column name 'AnotherColumn'.
Second Batch Starts
Second Batch Ends

请注意,第一个批次打印中没有一个print语句,因为批次未运行,但是在第二批次中,由于列的验证将推迟到该语句运行(并且永远不会运行,因此不会发生错误)。

如果要在现有对象上引用潜在的不存在的列,请使用sys.sp_executesql>:

CREATE TABLE dbo.SomeTable (SomeColumn int);
GO
PRINT N'Third Batch Starts';    
IF 1 = 0 BEGIN
    PRINT N'Entered first IF.';
    EXEC sys.sp_executesql N'INSERT INTO dbo.SomeTable (AnotherColumn) VALUES(1);';
END;
PRINT N'Third Batch Ends';    
GO

DROP TABLE dbo.SomeTable;

Third Batch Starts
Third Batch Ends

None of your statements are run, it isn't skipping the PRINT and RAISERROR and going to the INSERT 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 column NonExistentColumn, 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:

CREATE TABLE dbo.SomeTable (SomeColumn int);
GO
PRINT N'First Batch Starts';    
IF 1 = 0 BEGIN
    PRINT N'Entered first IF.';
    INSERT INTO dbo.SomeTable (AnotherColumn)
    VALUES(1);
END;
PRINT N'First Batch Ends';    
GO
PRINT N'Second Batch Starts';    
IF 1 = 0 BEGIN
    PRINT N'Entered second IF.';
    INSERT INTO dbo.AnotherTable (AnotherColumn)
    VALUES(1);
END;
PRINT N'Second Batch Ends';
GO

DROP TABLE dbo.SomeTable;

In the messages pane of SSMS you will get the following:

Msg 207, Level 16, State 1, Line 6
Invalid column name 'AnotherColumn'.
Second Batch Starts
Second Batch Ends

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:

CREATE TABLE dbo.SomeTable (SomeColumn int);
GO
PRINT N'Third Batch Starts';    
IF 1 = 0 BEGIN
    PRINT N'Entered first IF.';
    EXEC sys.sp_executesql N'INSERT INTO dbo.SomeTable (AnotherColumn) VALUES(1);';
END;
PRINT N'Third Batch Ends';    
GO

DROP TABLE dbo.SomeTable;

This simply prints the following:

Third Batch Starts
Third Batch Ends

db<>fiddle

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文