Use of Begin / End Blocks and the Go keyword in SQL Server?

What are the guidelines as to when to use begin and end blocks in SQL Server?

Also, what exactly does the Go keyword do?

158642 次浏览

您需要 BEGIN... END 来创建一个包含多个语句的块。所以,如果你想在一个 IF 语句的一个“分支”中做两件事,或者你想在 WHILE 循环的主体中做不止一件事,你需要用 BEGIN... END 把这些语句括起来。

GO 关键字不是 SQL 的一部分。它只被 QueryAnalyzer 用来将脚本划分为独立执行的“批”。

GO 就像是一个脚本的结尾。

可以有多个 CREATETABLE 语句,用 GO 分隔。这是一种将脚本的一部分与另一部分隔离的方法,但是要在一个块中提交所有脚本。


BEGIN 和 END 就像 C/+ +/# 、 Java 等语言中的{ and }。

They bound a logical block of code. I tend to use BEGIN and END at the start and end of a stored procedure, but it's not strictly necessary there. Where it IS necessary is for loops, and IF statements, etc, where you need more then one step...

IF EXISTS (SELECT * FROM my_table WHERE id = @id)
BEGIN
INSERT INTO Log SELECT @id, 'deleted'
DELETE my_table WHERE id = @id
END

GO 在 SQLServer 中不是关键字; 它是批分隔符。GO 结束一批语句。当您使用类似于 SQLCMD 的东西时,这尤其有用。假设您在命令行上输入 SQL 语句。您并不需要在每次结束语句时都执行该事件,因此在输入“ GO”之前,SQLServer 不执行任何操作。

同样,在批处理开始之前,通常需要有一些可见的对象。例如,假设您正在创建一个数据库,然后查询它。你不能写:

CREATE DATABASE foo;
USE foo;
CREATE TABLE bar;

因为对于执行 CREATE TABLE 的批处理,foo 不存在:

CREATE DATABASE foo;
GO
USE foo;
CREATE TABLE bar;

GO 结束一个批处理,你只需要很少在代码中使用它。请注意,如果在存储过程中使用它,那么在执行过程时将不会执行 GO 之后的代码。

对于任何要处理多行代码的过程类型语句,都需要 BEGIN 和 END。您将需要它们用于 WHILE 循环和游标(当然,如果可能的话,您将尽量避免使用它们)和 IF 语句(从技术上讲,您不需要它们用于只有一行代码的 IF 语句,但是如果您总是将它们放在 IF 之后,则更容易维护代码)。CASE 语句也使用 END,但没有 BEGIN。

BEGIN and END have been well answered by others.

正如 Gary 所指出的,GO 是一个批分隔符,被大多数 Microsoft 提供的客户端工具使用,如 isql、 sqlcmd、查询分析器和 SQL Server Management Studio。(至少有一些工具允许更改批分离器。我从未见过更换批分离器有什么用处。)

要回答何时使用 GO 的问题,需要知道 SQL 何时必须分成批。

Some statements must be the first statement of a batch.

select 1
create procedure #Zero as
return 0

在 SQLServer2000上,错误是:

Msg 111, Level 15, State 1, Line 3
'CREATE PROCEDURE' must be the first statement in a query batch.
Msg 178, Level 15, State 1, Line 4
A RETURN statement with a return value cannot be used in this context.

在 SQLServer2005上,该错误就没那么有用了:

Msg 178, Level 15, State 1, Procedure #Zero, Line 5
A RETURN statement with a return value cannot be used in this context.

因此,使用 GO将必须作为批处理开始的语句与脚本中在批处理开始之前的语句分开。

当运行一个脚本时,许多错误会导致批处理的执行停止,但是客户端将简单地发送下一个批处理,脚本的执行将不会停止。我经常在测试中使用它。我将以事务开始和回滚结束脚本,在中间做所有的测试:

begin transaction
go
... test code here ...
go
rollback transaction

That way I always return to the starting state, even if an error happened in the test code, the begin and rollback transaction statements being part of a separate batches still happens. If they weren't in separate batches, then a syntax error would keep begin transaction from happening, since a batch is parsed as a unit. And a runtime error would keep the rollback from happening.

Also, if you are doing an install script, and have several batches in one file, an error in one batch will not keep the script from continuing to run, which may leave a mess. (Always backup before installing.)

与 Dave Markel 指出的相关,有些情况下解析会失败,因为 SQL Server 正在数据字典中查找批处理中早期创建的对象,但是解析可以在运行任何语句之前进行。有时候这是个问题,有时候不是。我想不出一个好的例子。但是如果你曾经得到一个“ X 不存在”的错误,当它明显地存在时,由该语句分成批处理。

最后要注意的是,事务可以跨批处理。(见上文)变量不跨批处理。

declare @i int
set @i = 0
go
print @i


Msg 137, Level 15, State 2, Line 1
Must declare the scalar variable "@i".

经过今天与这个问题的斗争,我的观点是: BEGIN... END 括号代码就像 C 语言中的{ ... . }一样,例如 if... else 和循环的代码块

GO is (must be) used when succeeding statements rely on an object defined by a previous statement. USE database is a good example above, but the following will also bite you:

alter table foo add bar varchar(8);
-- if you don't put GO here then the following line will error as it doesn't know what bar is.
update foo set bar = 'bacon';
-- need a GO here to tell the interpreter to execute this statement, otherwise the Parser will lump it together with all successive statements.

在我看来,问题在于: SQL Server SQL Parser 与 Oracle 不同,它无法意识到您在第一行定义了一个新的符号,并且可以在以下几行中引用。直到遇到 GO 令牌,它才“看到”这个符号,GO 令牌告诉它从上一个 GO 开始执行前面的 SQL,此时这个符号被应用到数据库,并且对解析器来说是可见的。

为什么它不仅仅把分号当作一个语义突破,然后单独应用语句呢? 我不知道,也希望它这样做。我看到的唯一好处是,您可以在 GO 之前放置一个 print ()语句,如果任何语句失败,print 将不会执行。不过为了一点小小的收获,麻烦可就大了。