我正在探讨在存在语句级别问题时Sql Server“批处理”行为的界限。我阅读了this Technet article并通过我自己的一些实验再现了这个例子。
我想我明白SQL Server会在两次传递中尝试编译批处理。第一遍是尝试编译批处理中的每个语句,这些语句可能会在以后使用语句级重新编译执行期间成功失败。在这种情况下,SQL Server将接受批处理并开始执行它,根据需要逐行尝试语句级重新编译。
我不明白为什么以下两个例子表现不同。 我在SQL Server 2017和2008 R2上进行了相同测试的测试。
/* first example
assuming table foo does not exist before first run
*/
create table foo (i int);
insert into foo values (1);
insert into foo values (1,1); /* wrong - column mismatch */
go
select * from foo;
第一次运行:result是一个包含一行的新表(由select语句返回)。由于第二个插入语句的重新编译失败而引发错误(我相信在第二次传递期间)。 Column name or number of supplied values does not match table definition.
第二次运行:表格没有变化。 select语句仍返回一行。由于create table语句的编译失败而引发错误(我相信在第一次传递期间)。 There is already an object named 'foo' in the database.
第三次运行 - 这次使用create table语句注释掉:没有更改表。 select仍然返回一行。由于第二个插入语句的编译失败而引发错误(我相信这次第一次通过)。 Column name or number of supplied values does not match table definition.
请注意,第一次和第三次运行的错误完全相同,但它发生在不同的传递中,因此批次行为不同(按设计)。 这必须是因为在第一次运行期间第一批中的create table语句之后的语句的编译被延迟了(这是具有延迟的语句级重新编译工具的整点)。
现在 - 令人惊讶 - 以下示例表现不同。
/* second example
assuming tables foo and bar do not exist before first run
*/
create table foo (i int);
insert into foo values (1);
insert into bar values (1,1); /* wrong - different problem */
go
select * from foo;
首次运行:与第一个示例一样,创建了表foo并添加了一行。 select语句返回单行。在这种情况下引入的错误是:Invalid object name ‘bar’
,显然在第二次(重新编译)过程中被捕获。到目前为止,整体行为是一样的。
第二次运行:再次出现相同的行为 - 编译失败(第一次传递)在create table语句中,与第一个示例中的错误相同。There is already an object named 'foo' in the database.
因此,整体行为也是如此。
第三次运行 - 这次将create table语句注释掉:将一个新行添加到表foo ,然后抛出与第一次运行时相同的错误(第二次传递):{ {1}}我可以一遍又一遍地重新运行,每次都会在表foo中添加一个新行。
- 更新 - 一个简单的例子更好地展示了延迟名称解析的效果,在阅读了更多相关内容之后(参见tarheel的回答)
Invalid object name ‘bar’.
答案 0 :(得分:0)
这是因为deferred name resolution,在运行批处理之前只关心有效语法,而不检查对象(bar
)本身是否存在。
换句话说,因为foo
存在,所以第一批因为现有的表定义而看到insert into foo values (1,1);
无效语法,并在第一批Column name or number of supplied values does not match table definition
之前生成错误insert
{1}}语句运行。虽然第二批在查找第一个bar
语句运行之后的对象insert
之前没有看到错误。
通过在运行任何语句之前使用strict_checks
查找对象bar
,您可以使第二批行为与第一批行为相同。
SET STRICT_CHECKS ON
--create table foo (i int);
insert into foo values (1);
insert into bar values (1,1); /* wrong - different problem */
go
select * from foo;
<强>更新强>
描述正在发生的事情的另一种方式是:
在编译阶段,sql server假定语法有效,除非它有证据表明语法无效。验证语法后,批处理进入执行阶段,运行每一行,并按顺序处理错误。
批处理1(第三次运行 - GO
之前的代码)编译/执行将如下所示:
1)编译第一个插入(insert into foo values (1);
)并查看其有效语法,因为它与foo
模式匹配。
2)编译第二个插入(insert into foo values (1,1);
)并查看其无效语法,因为它与foo
模式不匹配。引发错误。
3)执行第一次插入 - 由于步骤2中的错误,从未发生过。
4)执行第二次插入 - 由于步骤2中的错误,从未发生过。
批处理2(第三次运行 - GO
之前的代码)编译/执行将如下所示:
1)编译第一个插入(insert into foo values (1);
)并查看它的有效语法,因为它与foo
模式匹配。
2)编译第二个插入(insert into bar values (1,1);
),其中没有要检查的bar
模式,因此它假定语法有效并且bar
表包含在执行插入之前将创建适当的模式。
3)执行第一次插入,成功,因为它是有效的语法。
4)执行第二次插入,引发错误。
批处理2,步骤2(粗体)是延迟名称解析发挥作用的地方。