98

I know that in SQL Server GO is considered a batch separator.

My question is: What is the point of having a batch separator? What benefit does it give you and why would you want to use it?

Example: I've often seen it used in SQL code as follows and I can't see why it would be considered a best practice. As far as I can tell the code would the same without all the GO statements:

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

(source: technet documentation):

Community
  • 1
  • 1
Zain Rizvi
  • 23,586
  • 22
  • 91
  • 133
  • 4
    possible duplicate of [What is the use of GO in SQL Server Management Studio?](http://stackoverflow.com/questions/2299249/what-is-the-use-of-go-in-sql-server-management-studio) – Johnny Bones Dec 20 '13 at 20:22
  • 8
    @JohnnyBones, no it's not the same (I even linked to that post in my question). That post basically says that GO is a batch separator, but my question is asking what a batch separator is good for – Zain Rizvi Dec 20 '13 at 20:24
  • Look at the answer provided by tvanfosson in that question (26 upvotes). – Johnny Bones Dec 20 '13 at 20:25
  • 3
    @JohnnyBones thanks for pointing out that answer. It gives some good additional background info, but doesn't answer the root question I had (heck, one person even left a comment on that question asking what batching scripts is good for) – Zain Rizvi Dec 20 '13 at 20:34
  • 1
    Definitely a duplicate of this... http://stackoverflow.com/questions/2668529/t-sql-go-statement/2668549#2668549 – sam yi Dec 20 '13 at 20:35
  • @samyi - The answer there is somewhat confused though. There is no need to separate into different batches to get smaller transactions. – Martin Smith Dec 20 '13 at 20:36
  • There are cases where if you want multiple statements in a single script, you must use a batch separator like GO. See my answer. – Paul Williams Dec 20 '13 at 21:19
  • @PaulWilliams - There are virtually no (if any) cases where you *must* use `GO`. Have you any examples where it can't be replaced with `EXEC`? – Martin Smith Dec 20 '13 at 21:23
  • @MartinSmith - When management says "Don't use dynamic SQL here." :) But seriously, no, dynamic SQL would work too, but a batch separator seems cleaner to me. – Paul Williams Dec 20 '13 at 21:30
  • Completely agree it is often cleaner. Syntax highlighting and intellisense are obviously completely lost when `EXEC`-ing strings. Just the word *must* overstates things! – Martin Smith Dec 20 '13 at 21:33
  • So, does "batch separator" means something like "file separator"? I mean, having the GO(s) in place is like having the SQL queries in different batch files (script files), and then executing them one after the other. – carloswm85 Jul 30 '21 at 15:59

4 Answers4

51

In the example there it is of no use whatsoever.

Lots of statements must be the only ones in the batch however.

Such as CREATE PROCEDURE.

Also often after making schema changes (e.g. adding a new column to an existing table) statements using the new schema must be compiled separately in a different batch.

Generally an alternative to submitting separate batches separated by GO is to execute the SQL in a child batch using EXEC

Martin Smith
  • 438,706
  • 87
  • 741
  • 845
  • 1
    That makes sense, thanks. I've also seen it used in database schema upgrade scripts after every single alter/create table operation even when the sequential operations did not depend on the previous one. Is there any reason to do that, or would that have been a practice to not make devs have to think to hard about their SQL – Zain Rizvi Dec 20 '13 at 20:28
  • 1
    @Zain - Well it doesn't do much harm (apart from increasing network round trips) and probably saves some errors where the operations do depend on previous ones. – Martin Smith Dec 20 '13 at 20:29
  • It sounds like SQL code would need to be split into two batches when the second batch operates on objects created by the first batch. In other words, when second batch requires the SQL planning engine to use artifacts that don't exist yet, which would screw up the planning. Is that correct? – Zain Rizvi Dec 20 '13 at 20:49
  • 7
    @Zain - As far as tables are concerned it is changes to existing tables that tend to be more problematic. `CREATE TABLE T(X INT);SELECT * FROM T` works fine as the `SELECT` is subject to deferred compile. But then after the table is created `ALTER TABLE T ADD Y INT;SELECT Y FROM T` would fail with an invalid column name error as that statement does not get compilation deferred. – Martin Smith Dec 20 '13 at 20:50
  • To me this still doesn't answer the question. "Lots of statements must be the only ones in the batch however" - but what is the point of having a batch? If the server has some internal limitation requiring "lots of statements" to be the only ones in a batch - intellisense can clearly detect the requirement, so why require me to write GO at all? Why not just run the internals the way they need to be done? To Martin - "compilation deferred"? Why should a SQL coder need to understand internal compilation processes? The question remains: What is the point of batching? – youcantryreachingme May 30 '19 at 00:04
  • Worse - "SQL Server provides commands that are not Transact-SQL statements, but are recognized by the sqlcmd and osql utilities and SQL Server Management Studio Code Editor" (MS site). In other words, if I remove all GO statements (even after ALTER, etc) I still have valid TSQL - but the primary tool for executing T-SQL against SQL Server will fault. – youcantryreachingme May 30 '19 at 00:05
  • About the only functional implications of `go` (reasons to want to use it) I can see in the docs are "The statements in the batch are then compiled into a single execution plan", or if you want to force local variables to be dropped from a scope of work. However the **requirement** to use `go` after things like `alter` to avoid errors is inexplicable - I can't see any reason to require that of a developer. Docs also say "Users must follow the rules for batches". It goes on "for example" but I can't find a definitive description of "the rules". – youcantryreachingme May 30 '19 at 00:15
  • Lastly "Applications based on the ODBC or OLE DB APIs receive a syntax error if they try to execute a GO command." Does this mean a script having ALTER, then SELECT (per example above) succeeds via ODBC but fails in SSMS? Is that because ODBC implicitly applies `go` after every statement? If so, does that mean you cannot use variables in ODBC scripts because they will implicitly fall out of scope after every statement's implicit `go`? So many questions. Cannot find answers. – youcantryreachingme May 30 '19 at 00:17
38

As TechNet says, GO it signifies the end of a SQL batch to the SQL utilities. For example, when SQL Server Management Studio encounters the batch separator, it knows all of the text so far is an independent SQL query.

We use a similar technique in our software. We keep all of our procs, schema scripts, data conversions, etc., in SQL script files (checked in to source control). When our installer reads one of these script files, GO tells our parser "you can run the SQL that you've already read".

The nice feature about a batch separator like GO is that you can include two SQL queries together in the same script that would normally cause an error. For example, try to drop and re-create the same stored procedure in the same script file:

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

create procedure sp_test as
begin
    select 1
end

If you run the above code, you will get an error:

Msg 156, Level 15, State 1, Procedure sp_test, Line 5 Incorrect syntax near the keyword 'begin'.

And SSMS will show you the error:

Incorrect syntax. 'CREATE PROCEDURE' must be the only statement in a batch.

Using a batch separator can help you get around this error:

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

This is very handy if, say, you want a single SQL script in source control to maintain a stored procedure or function. We use this pattern frequently.

Another interesting thing you can do is use it to run a query multiple times:

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

As the answers to this SO question demonstrate, you can also configure it to whatever you want. If you want to mess with your co-workers, set the batch separator to something like "WHERE" instead of "GO". Fun! :)

Community
  • 1
  • 1
Paul Williams
  • 16,585
  • 5
  • 47
  • 82
  • 15
    That kind of fun could be dangerous to one's health. – Dylan Brams Dec 20 '13 at 21:48
  • 4
    You mean GO gets around the error that only exists because GO exists? If Intellisense can detect GO is required, surely so can the server and just handle the issue. To me there is nothing logically wrong with "if exists, drop, and after this, create". The docs note all statements within a GO batch are compiled into a single execution plan - this is the reason those statements would fail, but again - if intellisense can detect it, surely the server can too and just deal with it. `go 10` is, in fact, useful - but not mandatory in the way `go` is required in your earlier example. – youcantryreachingme May 30 '19 at 00:25
  • @youcantryreachingme I do not know why SQL Server requires these batches to be separate. If you do not like the behavior, I suppose you could enter some feedback for the SQL Server team. It does seem to be a strange requirement to me, but I am sure there is a technical reason for it. – Paul Williams May 30 '19 at 15:04
  • @PaulWilliams - After venting all my questions in comments here (and on dba stack exchange), I kept pushing through Microsoft docs to try and understand. I think the "why" - the reasons - around batches and `go` depend on understanding the *implications* of batching - primarily that a single execution plan is prepared from the whole batch. So I finally decided to contribute my own answer to try and explain the "why" - see below, here: https://stackoverflow.com/a/56370223/3714936 – youcantryreachingme May 30 '19 at 23:41
37

What is the point of having a batch separator?

Having read many of the answers, and contributed to comments, here is what I think.

The real question is "What is the point of having a batch?"

There are 2 implications of batching that have some meaning, and there is an additional usage of go that can be useful:

1. All statements in a batch are compiled into a single execution plan

How this impacts you, as a SQL developer, I don't know. But there it is. The implication of this is that you can't have some statements within the same batch. For example, you cannot ALTER a table to add a column, then select that column in the same batch - because while compiling the execution plan, that column does not exist for selecting.

I think there is an open argument as to whether SQL Server should be able to detect this by itself without requiring developers to include go statements in their scripts. Further, the docs say ODBC connections may never issue a go command. It is not clear to me how a script run through ODBC would behave if it included the ALTER / SELECT example just given.

2. Locally declared variables exist only within the scope of the batch in which they were declared

These two points combined kind of suck. I have a script that creates and alters DB structures (tables, procedures, etc) and I want to declare variables at the start of the script that will be used to govern the behaviour of the script overall. As soon as I need to wrap up a batch (due to, say, an ALTER statement - see my point 1, above), those "config" variables fall out of scope and can't be used further down the script. My workaround is to create a table, persist the config variables into the table, then read from that table all the way through my script, then drop the table at the end (in case anyone else is facing this).

This second implication can actually be used to advantage - if your script is doing a lot of work and you simply want to clear out all your local variables, you can simply include a GO statement and then declare new variables (ie. and re-use the same names, if that's what you want).

3. GO has an optional parameter (named "count") which tells the server to repeat the batch actions multiple times

This usage seems to be nice additional functionality added on to the GO statement. I believe the initial or primary function of GO relates more to the compilation of a single execution plan, as mentioned in point 1 - otherwise the keyword may as well be something like REPEAT 10 - but repeat what? The batch. Without GO signifying a batch, a repeat command could only ever repeat the prior single statement. Therefore GO is a nice way to repeat batches.

Reference

All of this comes from trying to understand the MS documentation on GO. Many of the other answers - here, and on other questions - pick at pieces of the documentation but I think the documentation itself fails to really explain why there is a benefit to batching in the first place - hence my contribution to an already well-commented question.

Addendum

After writing the above, I did find the Rules for Using Batches mentioned by Microsoft in the GO documentation. The linked page explains that an execution plan consists of multiple statements. It also says that individual statements can be re-compiled into a new execution plan (ie by SQL Server, while processing the batch, automatically). So for example, following a statement to CREATE TABLE you might have an INSERT into that table. That INSERT statement will be recompiled after the table has been created in the prior statement.

This re-enforces the idea that SQL Server probably could detect those scenarios where an ALTER to a table is followed by a SELECT and that it needs to re-compile the SELECT (see my point 1 above), and possibly this is exactly what happens if using ODBC (see point 1 above).

None of this new information alters the 3 points given above. The link I just gave contains additional reading and ends with "the rules", which are these:

  • CREATE DEFAULT, CREATE FUNCTION, CREATE PROCEDURE, CREATE RULE, CREATE SCHEMA, CREATE TRIGGER, and CREATE VIEW statements cannot be combined with other statements in a batch. The CREATE statement must start the batch. All other statements that follow in that batch will be interpreted as part of the definition of the first CREATE statement.

  • A table cannot be changed and then the new columns referenced in the same batch.

  • If an EXECUTE statement is the first statement in a batch, the EXECUTE keyword is not required. The EXECUTE keyword is required if the EXECUTE statement is not the first statement in the batch.

youcantryreachingme
  • 1,065
  • 11
  • 17
5

Like Martain said, statements such as CREATE PROCEDURE must be the only ones in a batch.

For example, I use batch separators whenever I create stored procedures and add permissions to a certain user. If I left out the 'go' then I would end up with a stored procedure that grants rights every time it runs. This way I can write them at the same time and be sure that I'm not writing stored procedures that break when I call them. For example

create procedure [procedurename]
(parameters)
as begin

select prefname, lastname from people

end

go

grant execute on [procedurename] to [username]
Zain Rizvi
  • 23,586
  • 22
  • 91
  • 133
DaneEdw
  • 446
  • 1
  • 8
  • 18