为什么这些t-sql批处理表现不同?

时间:2018-01-24 22:25:07

标签: sql-server

我正在探讨在存在语句级别问题时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;
  1. 第一次运行:result是一个包含一行的新表(由select语句返回)。由于第二个插入语句的重新编译失败而引发错误(我相信在第二次传递期间)。 Column name or number of supplied values does not match table definition.

  2. 第二次运行:表格没有变化。 select语句仍返回一行。由于create table语句的编译失败而引发错误(我相信在第一次传递期间)。 There is already an object named 'foo' in the database.

  3. 第三次运行 - 这次使用create table语句注释掉:没有更改表。 select仍然返回一行。由于第二个插入语句的编译失败而引发错误(我相信这次第一次通过)。 Column name or number of supplied values does not match table definition.

  4. 请注意,第一次和第三次运行的错误完全相同,但它发生在不同的传递中,因此批次行为不同(按设计)。 这必须是因为在第一次运行期间第一批中的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;
    
    1. 首次运行:与第一个示例一样,创建了表foo并添加了一行。 select语句返回单行。在这种情况下引入的错误是:Invalid object name ‘bar’,显然在第二次(重新编译)过程中被捕获。到目前为止,整体行为是一样的。

    2. 第二次运行:再次出现相同的行为 - 编译失败(第一次传递)在create table语句中,与第一个示例中的错误相同。There is already an object named 'foo' in the database.因此,整体行为也是如此。

    3. 第三次运行 - 这次将create table语句注释掉:将一个新行添加到表foo ,然后抛出与第一次运行时相同的错误(第二次传递):{ {1}}我可以一遍又一遍地重新运行,每次都会在表foo中添加一个新行。

    4. - 更新 - 一个简单的例子更好地展示了延迟名称解析的效果,在阅读了更多相关内容之后(参见tarheel的回答)

      Invalid object name ‘bar’.

1 个答案:

答案 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(粗体)是延迟名称解析发挥作用的地方。