SQL Server:什么是批处理语句(即使用"GO")有用?

Zai*_*zvi 67 t-sql sql-server

我知道在SQL Server中GO 被认为是批处理分隔符.

我的问题是:有一个批处理分隔符有什么意义?它给你带来了什么好处,为什么要使用它?

示例:我经常看到它在SQL代码中使用如下,我不明白为什么它被认为是最佳实践.据我所知,代码在没有所有GO语句的情况下都是一样的:

USE AdventureWorks2012;
GO
BEGIN TRANSACTION;
GO
IF @@TRANCOUNT = 0
BEGIN
    SELECT FirstName, MiddleName 
    FROM Person.Person WHERE LastName = 'Adams';
    ROLLBACK TRANSACTION;
    PRINT N'Rolling back the transaction two times would cause an error.';
END;
ROLLBACK TRANSACTION;
PRINT N'Rolled back the transaction.';
GO
Run Code Online (Sandbox Code Playgroud)

(来源:technet文档):

Mar*_*ith 37

在这个例子中,没有任何用处.

然而,许多陈述必须是批次中的唯一陈述.

CREATE PROCEDURE.

通常在进行模式更改(例如,向现有表添加新列)之后,使用新模式的语句必须在不同的批处理中单独编译.

通常,提交分隔的单独批次的替代方法GO是使用子批处理执行SQLEXEC

  • @Zain - 就表而言,现有表的更改往往更有问题.`CREATE TABLE T(X INT); SELECT*FROM T`工作正常,因为`SELECT`受延迟编译.但是在创建表之后`ALTER TABLE T ADD Y INT; SELECT Y FROM T`将失败并且列名无效,因为该语句没有得到编译延迟. (6认同)

Pau*_*ams 29

正如TechNet所说,GO它表示SQL批处理结束SQL实用程序.例如,当SQL Server Management Studio遇到批处理分隔符时,它知道到目前为止所有文本都是独立的SQL查询.

我们在软件中使用了类似的技术.我们将所有的proc,模式脚本,数据转换等保存在SQL脚本文件中(签入源代码管理).当我们的安装程序读取其中一个脚本文件时,GO会告诉我们的解析器"您可以运行已经读过的SQL".

关于批处理分隔符的一个很好的功能GO是,您可以在同一脚本中包含两个SQL查询,这些脚本通常会导致错误.例如,尝试在同一个脚本文件中删除并重新创建相同的存储过程:

if exists (select * from sys.procedures where name = 'sp_test')
    drop procedure sp_test

create procedure sp_test as
begin
    select 1
end
Run Code Online (Sandbox Code Playgroud)

如果您运行上面的代码,您将收到一个错误:

消息156,级别15,状态1,过程sp_test,行5关键字'begin'附近的语法不正确.

SSMS会向您显示错误:

语法不正确.'CREATE PROCEDURE'必须是批次中唯一的声明.

使用批处理分隔符可以帮助您解决此错误:

if exists (select * from sys.procedures where name = 'sp_test')
    drop procedure sp_test
GO
create procedure sp_test as
begin
    select 1
end
Run Code Online (Sandbox Code Playgroud)

如果您希望源代码管理中的单个SQL脚本维护存储过程或函数,则这非常方便.我们经常使用这种模式.

您可以做的另一个有趣的事情是使用它多次运行查询:

INSERT INTO MyTable (...) ...
GO 10 -- run all the above 10 times!
Run Code Online (Sandbox Code Playgroud)

正如这个SO问题的答案所示,您也可以将其配置为您想要的任何内容.如果您想弄乱您的同事,请将批处理分隔符设置为"WHERE"而不是"GO".有趣!:)

  • 这种乐趣可能危害一个人的健康. (11认同)
  • 你的意思是 GO 绕过了只因为 GO 存在而存在的错误?如果 Intellisense 可以检测到 GO 是必需的,那么服务器当然也可以,并且只需处理这个问题。对我来说,“如果存在,删除,然后创建”在逻辑上没有任何错误。文档指出 GO 批处理中的所有语句都被编译成一个单一的执行计划——这就是这些语句会失败的原因,但同样——如果智能感知可以检测到它,服务器当然也可以并处理它。`go 10` 事实上很有用 - 但不是你之前例子中需要 `go` 的强制方式。 (3认同)

you*_*gme 21

有一个批处理分离器有什么意义?

阅读了许多答案并发表评论后,这就是我的想法。

真正的问题是“拥有一批有什么意义?”

批处理有两个含义,它们具有一定的意义,还有一个额外的用法go可能很有用:

1.批处理中的所有语句都编译成一个执行计划

作为 SQL 开发人员,我不知道这对您有何影响。但它就在那里。这意味着您不能在同一批次中包含某些语句。例如,您不能在ALTER一个表中添加一列,然后select在同一批次中添加该列- 因为在编译执行计划时,该列不存在可供选择。

我认为对于 SQL Server 是否应该能够在不需要开发人员go在其脚本中包含语句的情况下自行检测到这一点,存在一个公开的争论。此外,文档说 ODBC 连接可能永远不会发出go命令。如果包含刚刚给出的ALTER/SELECT示例,我不清楚通过 ODBC 运行的脚本的行为方式。

2. 局部声明的变量只存在于声明它们的批处理范围内

这两点结合起来有点糟糕。我有一个创建和更改数据库结构(表、过程等)的脚本,我想在脚本的开头声明变量,这些变量将用于管理脚本的整体行为。一旦我需要结束一个批处理(例如,由于一个ALTER语句 - 参见我上面的第 1 点),那些“配置”变量就超出了范围,无法在脚本的进一步下方使用。我的解决方法是创建一个表,将配置变量保存到表中,然后通过我的脚本一直从该表中读取,然后在最后删除该表(以防其他人面临这种​​情况)。

这第二个含义实际上可以被利用 - 如果您的脚本正在做大量工作并且您只想清除所有局部变量,您可以简单地包含一个GO语句,然后声明新变量(即。并重新使用相同的名字,如果这是你想要的)。

3. GO 有一个可选参数(名为“count”),它告诉服务器多次重复批处理操作

这种用法似乎是添加到GO语句中的不错的附加功能。我相信 的初始或主要功能GO更多地与单个执行计划的编译有关,如第 1 点所述 - 否则关键字也可能类似于REPEAT 10- 但重复什么?批次。在不GO表示批处理的情况下,重复命令只能重复之前的单个语句。因此GO是重复批次的好方法。

参考

所有这些都来自于试图理解GO 上MS 文档。许多其他答案 - 在这里以及其他问题 - 选择文档的一部分,但我认为文档本身并没有真正解释为什么首先批处理有好处 - 因此我对已经得到很好评论的贡献题。

附录

写完上面的内容后,我确实在文档中找到了微软提到的使用批处理规则GO。链接页面解释了一个执行计划由多个语句组成。它还表示可以将单个语句重新编译为新的执行计划(即由 SQL Server 在处理批处理时自动)。因此,例如,按照对CREATE TABLE您的陈述可能会INSERT进入该表。这INSERT表已事先声明在创建后声明将被重新编译。

这再次强化了这样一种想法,即 SQL Server 可能可以检测ALTER到表后跟 aSELECT并且它需要重新编译SELECT(参见我上面的第 1 点)的那些场景,并且可能这正是使用 ODBC 时发生的情况(见上文第 1 点)。

这些新信息都不会改变上面给出的 3 点。我刚刚给出的链接包含附加阅读并以“规则”结尾,它们是:

  • CREATE DEFAULT、CREATE FUNCTION、CREATE PROCEDURE、CREATE RULE、CREATE SCHEMA、CREATE TRIGGER 和 CREATE VIEW 语句不能与批处理中的其他语句组合。CREATE 语句必须启动批处理。该批处理中的所有其他语句将被解释为第一个 CREATE 语句定义的一部分。

  • 不能更改表,然后在同一批次中引用新列。

  • 如果 EXECUTE 语句是批处理中的第一条语句,则不需要 EXECUTE 关键字。如果 EXECUTE 语句不是批处理中的第一条语句,则需要 EXECUTE 关键字。

  • 这非常有用。感谢您花时间以井然有序的方式分享您所学到的知识。 (2认同)

Dan*_*Edw 5

就像Martain所说的那样,CREATE PROCEDURE等语句必须是批量中唯一的.

例如,每当我创建存储过程并向某个用户添加权限时,我都会使用批处理分隔符.如果我省略了'go',那么我最终会得到一个存储过程,每次运行时都会授予权限.通过这种方式,我可以同时编写它们,并确保在编写它们时我不会编写存储过程.例如

create procedure [procedurename]
(parameters)
as begin

select prefname, lastname from people

end

go

grant execute on [procedurename] to [username]
Run Code Online (Sandbox Code Playgroud)