【问题标题】:SQL Server: What are batching statements (i.e. using "GO") good for?SQL Server:批处理语句(即使用“GO”)有什么用?
【发布时间】:2014-01-09 18:29:30
【问题描述】:

我知道在 SQL Server 中 GO is considered a batch separator

我的问题是:使用批次分离器有什么意义?它能给您带来什么好处,您为什么要使用它?

示例:我经常看到它在 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 documentation):

【问题讨论】:

  • @JohnnyBones,不,它不一样(我什至在我的问题中链接到那个帖子)。那篇文章基本上说 GO 是一个批处理分隔符,但我的问题是问批处理分隔符有什么用
  • 查看 tvanfosson 在该问题中提供的答案(26 票)。
  • @JohnnyBones 感谢您指出这个答案。它提供了一些很好的附加背景信息,但没有回答我的根本问题(哎呀,有人甚至在这个问题上留下了评论,询问批处理脚本有什么好处)

标签: sql-server tsql


【解决方案1】:

在这个例子中它没有任何用处。

许多语句必须是批处理中唯一的语句。

CREATE PROCEDURE

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

通常,提交由GO 分隔的单独批处理的替代方法是使用EXEC 在子批处理中执行 SQL

【讨论】:

  • 有道理,谢谢。我还看到它在每次更改/创建表操作后用于数据库模式升级脚本,即使顺序操作不依赖于前一个操作也是如此。是否有任何理由这样做,或者这是一种不让开发人员不得不认真考虑他们的 SQL 的做法
  • @Zain - 好吧,它不会造成太大的伤害(除了增加网络往返次数),并且可能会在操作确实依赖于以前的错误的情况下节省一些错误。
  • 当第二批对第一批创建的对象进行操作时,听起来 SQL 代码需要分成两批。换句话说,当第二批需要 SQL 计划引擎使用尚不存在的工件时,这会搞砸计划。对吗?
  • @Zain - 就表格而言,对现有表格的更改往往会带来更多问题。 CREATE TABLE T(X INT);SELECT * FROM T 工作正常,因为 SELECT 需要延迟编译。但是在创建表之后,ALTER TABLE T ADD Y INT;SELECT Y FROM T 将失败并出现无效列名错误,因为该语句没有延迟编译。
  • 对我来说,这仍然不能回答问题。 “但是,许多语句必须是批处理中唯一的语句”-但是批处理的意义何在?如果服务器有一些内部限制,要求“大量语句”是批处理中唯一的语句——intellisense 可以清楚地检测到需求,那为什么还要我写 GO 呢?为什么不按照他们需要的方式运行内部?对马丁-“编译延迟”?为什么 SQL 编码员需要了解内部编译过程?问题仍然存在:批处理的意义何在?
【解决方案2】:

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

我们在我们的软件中使用了类似的技术。我们将所有的过程、模式脚本、数据转换等保存在 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 行语法不正确 靠近关键字“开始”。

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!

正如the answers to this SO question 演示的那样,您还可以将其配置为您想要的任何内容。如果您想惹恼您的同事,请将批处理分隔符设置为“WHERE”而不是“GO”。乐趣! :)

【讨论】:

  • 那种乐趣可能会危害一个人的健康。
  • 你的意思是 GO 绕过了只因为 GO 存在而存在的错误?如果 Intellisense 可以检测到 GO 是必需的,那么服务器当然也可以检测到,并且只需处理该问题。对我来说,“如果存在,删除,然后创建”在逻辑上没有任何错误。文档指出,GO 批处理中的所有语句都被编译成一个执行计划——这就是这些语句失败的原因,但同样——如果智能感知可以检测到它,服务器肯定也可以处理它。 go 10 实际上是有用的 - 但不是强制性的,就像您之前的示例中需要 go 一样。
  • @youcantryreachingme 我不知道为什么 SQL Server 要求这些批次是分开的。如果您不喜欢这种行为,我想您可以为 SQL Server 团队输入一些反馈。这对我来说似乎是一个奇怪的要求,但我确信这是有技术原因的。
  • @PaulWilliams - 在此处(以及在 dba 堆栈交换上)在 cmets 中发泄我的所有问题后,我一直在推动 Microsoft 文档以尝试理解。我认为“为什么”——原因——围绕批处理和go 取决于理解批处理的含义——主要是从整个批处理中准备一个执行计划。所以我最终决定贡献我自己的答案来尝试解释“为什么” - 见下文,这里:stackoverflow.com/a/56370223/3714936
【解决方案3】:

使用批次分离器有什么意义?

阅读了许多答案并为 cmets 做出了贡献,这就是我的想法。

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

批处理有 2 个含义有一定意义,另外还有一个有用的 go 用法:

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

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

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

2。本地声明的变量只存在于声明它们的批处理范围内

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

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

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

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

参考

所有这一切都来自于尝试理解MS documentation on GO。许多其他答案 - 在这里和其他问题 - 挑选了一些文档,但我认为文档本身并没有真正解释为什么首先批处理有好处 - 因此我对已经很好评论的贡献问题。

附录

写完上面,我确实在GO文档中找到了微软提到的Rules for Using Batches。链接页面说明执行计划由多个语句组成。它还说可以将单个语句重新编译成新的执行计划(即由 SQL Server 自动处理批处理)。例如,在对CREATE TABLE 的声明之后,您可能会在该表中添加一个INSERTINSERT 语句将在前面的语句中创建表后重新编译。

这再次强化了这样的想法,即 SQL Server 可能可以检测到表的 ALTER 后面跟着 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 关键字。

【讨论】:

  • 这非常有用。感谢您抽出宝贵时间以井然有序的方式分享您学到的知识。
【解决方案4】:

就像 Martain 说的,CREATE PROCEDURE 等语句必须是批处理中唯一的语句。

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

create procedure [procedurename]
(parameters)
as begin

select prefname, lastname from people

end

go

grant execute on [procedurename] to [username]

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-02-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-13
    • 2014-10-23
    • 1970-01-01
    • 2013-11-26
    相关资源
    最近更新 更多