什么时候需要在SQL Server中使用开始/结束块和Go关键字?


Answers:


116

GO就像脚本的结尾。

您可能有多个CREATE TABLE语句,以GO分隔。这是一种将脚本的一部分与另一部分隔离开来的方法,但是将其全部提交到一个块中。


BEGIN和END就像C / ++ /#,Java等中的{和}。

他们绑定了逻辑代码块。我倾向于在存储过程的开始和结束时使用BEGIN和END,但是在那儿并不一定要使用。对于循环和IF语句等需要的地方,那么您需要执行多个步骤...

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

您是否尝试过创建没有BEGIN和END的SP?IIRC,SP中仅包含第一行,其余代码仅在其中执行,然后...
cjk 2010年

2
这当然不是我从SQL Server 2000起的经验。
MatBailie

1
BEGIN和END是否也定义了新范围?
samis

2
是。在外部声明的所有内容在内部都是可见的,但是在内部声明的所有内容将超出END的范围。
MatBailie '18

36

您需要BEGIN ... END创建一个跨多个语句的块。因此,如果您想在IF语句的一个``分支''中做两件事,或者您想在WHILE循环的主体中做不止一件事情,则需要在这些语句中加上BEGIN ...结束。

GO关键字不是SQL的一部分。它仅由查询分析器用来将脚本分为独立执行的“批”。


28

GO不是SQL Server中的关键字。这是一个批处理分隔符。GO结束了一批语句。当您使用类似SQLCMD的东西时,这特别有用。假设您在命令行中输入SQL语句。您不一定希望每次结束语句时都执行该操作,因此SQL Server在您输入“ GO”之前不会执行任何操作。

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

CREATE DATABASE foo;
USE foo;
CREATE TABLE bar;

因为对于创建CREATE TABLE的批处理来说foo不存在。您需要这样做:

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

13

BEGIN和END得到了他人的良好回答。

正如Gary所指出的,GO是批处理分隔符,由Microsoft提供的大多数客户端工具(例如isql,sqlcmd,查询分析器和SQL Server Management Studio)使用。(至少某些工具允许更改批处理分隔符。我从未见过用于更改批处理分隔符的用法。)

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

某些语句必须是批次的第一条语句。

select 1
create procedure #Zero as
    return 0

在SQL Server 2000上,错误为:

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.

在SQL Server 2005上,该错误不太有用:

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

这样,即使测试代码中发生错误,我也始终返回到起始状态,仍然会发生分别作为单独批处理的一部分的begin和rollback事务语句。如果它们不是分开的批次,则语法错误将阻止开始事务的发生,因为批次被解析为一个单元。并且运行时错误将阻止回滚的发生。

另外,如果您正在执行安装脚本,并且在一个文件中有多个批处理,则一个批处理中的错误将使脚本无法继续运行,这可能会造成混乱。(在安装前始终备份。)

与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".

1
这就是我所需要的,谢谢:“事务可以跨越批处理。变量不能跨越批处理。”
加里

3

GO结束了一批,您只需要很少在代码中使用它。请注意,如果在存储的proc中使用它,则在执行proc时,GO之后的代码将不会执行。

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


GO之后是否会针对存储的proc存储任何代码?不会像GO之后不存在代码那样处理CREATE或ALTER语句吗?然后,执行GO之后的代码,就好像它是自己的脚本一样?
MatBailie

游标与它有什么关系?
加里·麦吉尔

3

在解决了这个问题之后,今天我的看法是:BEGIN ... END括号中的代码就像{....}在C语言中一样,例如if ... else和loops的代码块

当后续语句依赖于前一个语句定义的对象时,将(必须)使用GO。USE数据库是上面的一个很好的示例,但是以下内容也会对您造成影响:

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.

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

为什么它不只是将分号视为语义中断并单独应用语句,我不知道并且希望如此。我唯一能看到的好处是,您可以在GO之前放置一条print()语句,如果任何一条语句失败,将无法执行打印。虽然麻烦很多,但收获不大。

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.