SQL Server:批处理语句(即使用“ GO”)有什么好处?


76

我知道在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

(来源:technet文档):



7
@JohnnyBones,不,这不一样(我什至链接到我的问题中的帖子)。该帖子基本上说GO是批处理分离器,但是我的问题是问批处理分离器有什么好处
Zain Rizvi 2013年

查看tvanfosson在该问题中提供的答案(26票赞成票)。
约翰尼·博恩斯

3
@JohnnyBones感谢您指出该答案。它提供了一些很好的其他背景信息,但没有回答我的根本问题(哎呀,一个人甚至在该问题上留下了评论,询问哪些批处理脚本是好的)
Zain Rizvi 2013年

Answers:


40

在该示例中,它毫无用处。

但是,很多语句必须是批次中唯一的语句。

CREATE PROCEDURE

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

通常,提交由分隔的单独批处理的替代方法GO是使用以下命令在子批处理中执行SQLEXEC


1
这很有意义,谢谢。我还看到它在每个单独的alter / create表操作之后都在数据库模式升级脚本中使用,即使顺序操作不依赖于前一个操作也是如此。是否有任何理由这样做,或者是一种惯例,使开发人员不必认真思考自己的SQL
Zain Rizvi 2013年

1
@Zain-好吧(除了增加网络往返次数)并没有多大危害,并且可以节省一些错误,而这些错误确实取决于先前的操作。
马丁·史密斯

听起来当第二批处理由第一批创建的对象时,SQL代码需要分为两批。换句话说,当第二批需要SQL计划引擎使用尚不存在的工件时,这会破坏计划。那是对的吗?
Zain Rizvi 2013年

7
@Zain-就表而言,对现有表的更改往往会带来更多问题。CREATE TABLE T(X INT);SELECT * FROM T可以SELECT延迟编译,因此效果很好。但是,在创建表之后,该列ALTER TABLE T ADD Y INT;SELECT Y FROM T将失败,并显示无效的列名错误,因为该语句不会延迟编译。
马丁·史密斯

对我来说,这仍然无法回答问题。“但是,批处理中只有很多语句”-但是批处理有什么意义呢?如果服务器有一些内部限制,要求“大量语句”是批处理中的唯一语句,那么intellisense可以清楚地检测到该要求,那么为什么要求我完全编写GO?为什么不按照需要的方式运行内部结构呢?给马丁-“推迟编译”?SQL程序员为什么需要了解内部编译过程?问题仍然存在:批处理的目的是什么?
youcantryreachingme

30

正如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

如果运行上面的代码,则会收到错误消息:

消息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

如果您要在源代码管理中使用单个SQL脚本来维护存储过程或函数,这将非常方便。我们经常使用这种模式。

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

INSERT INTO MyTable (...) ...
GO 10 -- run all the above 10 times!

正如该SO问题的答案所示,您还可以将其配置为所需的任何内容。如果您想惹恼您的同事,请将批次分隔符设置为“ WHERE”而不是“ GO”。好玩!:)


13
这种乐趣可能会危害人的健康。
Dylan Brams

2
您的意思是GO会因为GO存在而绕开仅存在的错误?如果Intellisense可以检测到GO,那么服务器肯定也可以,只需要解决问题即可。对我来说,“如果存在,删除,然后再创建”在逻辑上没有错。文档指出,GO批处理中的所有语句都被编译到一个执行计划中-这是这些语句失败的原因,但是再次-如果intellisense可以检测到它,那么服务器当然也可以处理它。go 10实际上是有用的-但不是go您先前示例中所要求的强制方式。
youcantryreachingme,

@youcantryreachingme我不知道为什么SQL Server要求将这些批处理分开。如果您不喜欢这种行为,我想您可以为SQL Server团队输入一些反馈。对我来说,这确实是一个奇怪的要求,但是我确信这是有技术原因的。
保罗·威廉姆斯

@PaulWilliams-在这里(以及在dba堆栈交换中)在评论中提出所有问题之后,我一直在努力阅读Microsoft文档以尝试并理解。我想,“为什么” -原因-围绕批次,go取决于理解含义配料-主要是单个执行计划从整批准备。因此,我最终决定贡献自己的答案来尝试解释“为什么”-参见下文,这里:stackoverflow.com/a/56370223/3714936
youcantryreachingme,

14

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

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

真正的问题是“批量生产有什么意义?”

批处理有2个含义,它们有一定的意义,还有一些额外的用法go可能会有用:

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

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

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

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

这两点结合起来很烂。我有一个用于创建和更改数据库结构(表,过程等)的脚本,并且我想在脚本的开头声明变量,这些变量将用于总体上控制脚本的行为。一旦我需要包装一个批处理(例如,由于一条ALTER语句-参见我的观点1),那些“ config”变量就超出范围,无法在脚本中进一步使用。我的解决方法是创建一个表,将config变量持久保存到表中,然后通过我的脚本从该表中读取所有内容,然后将表放到最后(以防其他人面对该表)。

第二个含义实际上可以被利用-如果您的脚本正在做很多工作,而您只想清除所有局部变量,则可以简单地包含一条GO语句,然后声明新变量(即重新使用相同的名称(如果需要的话)。

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

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

参考

所有这些都来自试图了解GO上MS文档。许多其他答案-在这里和其他问题上-都是文档的一部分,但我认为文档本身并不能真正解释为什么批处理首先有好处-因此,我对已有评论的内容做出了贡献题。

附录

在写完上面的内容之后,我确实在文档中找到了Microsoft提到的“使用批处理规则”GO。链接的页面说明执行计划由多个语句组成。它还说,可以将各个语句重新编译为新的执行计划(即,通过SQL Server在自动处理批处理的同时)。因此,例如,向CREATE TABLE您声明之后,INSERT该表中可能会有一个。这INSERT表已事先声明在创建后声明将被重新编译。

这加强了这样的想法,即SQL Server可能会检测ALTER到在表后面紧接a的情况SELECT,并且它需要重新编译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关键字。


这是非常有用的。感谢您抽出宝贵的时间以井井有条的方式分享您学到的知识。
NetherGranite

5

就像Martain所说的那样,诸如CREATE PROCEDURE之类的语句必须是批处理中唯一的语句。

例如,每当我创建存储过程并向特定用户添加权限时,便使用批处理分隔符。如果我忽略了“执行”,那么我将得到一个存储过程,该存储过程每次运行都会授予权限。这样,我可以同时编写它们,并确保没有编写调用它们时会中断的存储过程。例如

create procedure [procedurename]
(parameters)
as begin

select prefname, lastname from people

end

go

grant execute on [procedurename] to [username]
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.