Using “GO” to Structure T-SQL Batches

If you spend much time writing T-SQL scripts, you’ve probably seen the GO keyword. It looks like a T-SQL command, but it’s not really part of T-SQL. Instead, it’s a batch separator recognized by SQL Server Management Studio (SSMS) and other client tools. When you hit the “Execute” button, any GO keyword in your script tells the tool that this is the end of a batch, and to send what came before it to SQL Server as one unit.

Many scripts will run fine without the GO keyword, but others will fail miserably. Understanding how GO works can save you from frustrating errors and unexpected behavior.

How Batches Work

SQL Server processes statements in batches. A batch is essentially a group of statements that SQL Server compiles and executes together. Some statements, like CREATE PROCEDURE, CREATE VIEW, or variable declarations, can only exist cleanly at the start of a batch. If you pile everything together without using GO, you’ll likely see errors you weren’t expecting.

If you’ve only ever run single queries, chances are you’ve never had a problem. It’s only when you try to combine multiple operations into a single script that you might start running into errors.

However, it all depends on the statements you’re trying to combine. Not all combined statements will fail without GO. there are many cases where we can combine multiple statements into a single batch, and therefore omit the GO keyword. Other times, we’ll need to include it.

Example: Omitting GO

Take this script for instance:

DROP TABLE IF EXISTS TestTable

CREATE TABLE TestTable
(
    Id INT PRIMARY KEY,
    Name NVARCHAR(50)
)

INSERT INTO TestTable (Id, Name)
VALUES (1, 'Sample')

This script contains three different statements. If you run this in one shot without GO, SQL Server sees it all as one batch. In this case there’s no problem – the script will run without error.

But now let’s try the following:

DROP TABLE IF EXISTS TestTable

CREATE TABLE TestTable
(
    Id INT PRIMARY KEY,
    Name NVARCHAR(50)
)

CREATE PROCEDURE GetTestTable
AS
BEGIN
    SELECT * FROM TestTable
END

Output:

Msg 111, Level 15, State 1, Procedure GetTestTable, Line 11
'CREATE/ALTER PROCEDURE' must be the first statement in a query batch.

This time an error is returned. That’s because SQL Server requires that if we’re going to use the CREATE PROCEDURE statement, it must be the first statement in a query batch.

The Fix: Using GO

Add a GO where the logical break is needed:

DROP TABLE IF EXISTS TestTable
GO

CREATE TABLE TestTable
(
    Id INT PRIMARY KEY,
    Name NVARCHAR(50)
)
GO

CREATE PROCEDURE GetTestTable
AS
BEGIN
    SELECT * FROM TestTable
END
GO

Now the first batch drops the table (if it exists), the second creates the table, and the third batch creates the procedure. SQL Server is fine with this, because we used the GO keyword to separate each statement into its own batch.

In this case, it’s the CREATE PROCEDURE statement that’s the main issue. We could have removed the other two GO keywords and left just one – the one before the CREATE PROCEDURE statement:

DROP TABLE IF EXISTS TestTable

CREATE TABLE TestTable
(
    Id INT PRIMARY KEY,
    Name NVARCHAR(50)
)
GO

CREATE PROCEDURE GetTestTable
AS
BEGIN
    SELECT * FROM TestTable
END

However, now that the procedure already exists, if we were to run the script again, we’d need to add a DROP PROCEDURE statement to drop the existing one (otherwise we’ll get another error, due to the procedure already existing).

DROP TABLE IF EXISTS TestTable

CREATE TABLE TestTable
(
    Id INT PRIMARY KEY,
    Name NVARCHAR(50)
)
GO

DROP PROCEDURE IF EXISTS GetTestTable
GO

CREATE PROCEDURE GetTestTable
AS
BEGIN
    SELECT * FROM TestTable
END

And because CREATE PROCEDURE must be the first statement in a batch, we needed to follow the DROP PROCEDURE statement with the GO keyword.

This leaves the script slightly inconsistent. There are GO statements in some parts but not in others. However, they’re only in the parts that require it. You’ll need to decide whether or not to include GO only when required, or simply include it even when not required, in order to keep things looking consistent.