Can we use 'GO' multiple times in SQL Transaction? - sql

Can We use GO statement mutiple times in a SQL Transaction. I am having a long T-SQL script and I want to run it in a SQL Transaction. If all goes well then I will commit otherwise will rollback.
But, While running that query I got error like 'create function must be the only statement in the batch'. As I am creating and dropping many Functions and Procedures in that.
I have not used GO anywhere in the script. My question is that - Can I use multiple times GO statement in that long script. Because, GO creates a batch and if batch executes successfully first time but fails next time then will rollback transaction statement be able to actually rollback that has been executed ?
Structure of my script looks like :
PRINT 'Transaction Started'
BEGIN TRY
BEGIN TRAN
Drop Function
....
....
Create Function
....
....
Drop Procedure
....
....
Lots of statements
....
....
COMMIT TRAN
PRINT 'Transaction Succeeded'
END TRY
BEGIN CATCH
PRINT 'Transaction Failed'
IF(##TRANCOUNT > 0)
ROLLBACK TRAN
END CATCH
I am creating this script to migrate some changes from newDB to oldDB in a single script.

You are mixing concepts. GO is not a Transact-SQL concept, not part of the language, and not understood by SQL Server. GO is the tools batch delimiter. sqlcmd.exe and SSMS both are using, by default, GO as the batch delimiter. The batch delimiter is used to identify the individual batches inside the SQL source file. The client tool sends to the server one batch at a time (of course, omitting the delimiter).
Transactions can span batches. TRY/CATCH blocks cannot. CREATE/ALTER statements must be the only statement in a batch (comments are not statements, and statements contained in a function procedure body are,well, contained).
Something similar to what you want to do can be achieved by starting a transaction and abortign the execution on first error (-b at sqlcmd.exe start, or use :on error exit in SSMS).
But doing DDL inside long transactions is not going to work. Specially if you plan to mix it with DML. Most corruptions I had to investigate come from this combination (Xact, DDL + DML, rollback). I strongly recommend against it.
The sole way to deploy schema updates safely is to take a backup, deploy, restore from backup if something goes wrong.
Note that what Dan recommends (dynamic SQL) works because sp_executesql starts a new, inner, batch. This batch will satisfy the CREATE/ALTER restrictions.

Note that GO is not a SQL keyword. It is a client-side batch separator used by SQL Server Management Studio and other client tools.
GO has no effect on transaction scope. BEGIN TRAN will start a transaction on the current connection. COMMIT and ROLLBACK will end the transaction. You can execute as many statements as you want in-between. GO will execute the statements separately.
As specified by MSDN:
A TRY…CATCH construct cannot span multiple batches.
So BEGIN TRY, END TRY, BEGIN CATCH, and END CATCH cannot be separated into separate batches by a GO separator. They must appear in the same query.
If you do try to include a batch separator in a TRY/CATCH statement like the invalid SQL below:
begin try
go
end try
begin catch
go
end catch
This will execute 3 different queries that return syntax errors:
1) begin try
Msg 102, Level 15, State 1, Line 1
Incorrect syntax near 'begin'.
2) end try begin catch
Msg 102, Level 15, State 1, Line 3
Incorrect syntax near 'try'.
3) end catch
Msg 102, Level 15, State 1, Line 6
Incorrect syntax near 'catch'.

GO is a nice keyword to use. The GO will complete the last code block and continue on to the next block. Yes you can use multiple GOs in a statement to break it up into multiple batches. But it would be better to use try/catch logic with a combination of GOs since you are doing transaction based logic. https://msdn.microsoft.com/en-us/library/ms175976.aspx this site gives you some examples on how to use it and if you run into a hitch you can output that error and continue on if you choose.

Related

roll back a batch is not easy?

After two days tests on batch, roll back and try ..catch, my mind is still vague. I separate what I was doing into two step in order to clear my question.
1. roll back a batch
As online book explains, in a batch, executed statements cannot be roll back only except the batch is in a transaction and error in the batch cause the transaction is roll back.
So I put the batch into a transaction like
begin transaction
create table A ...
insert into A values...
insert into A values... (error here!)
insert into A values...
GO
rollback
This works with error output and no table was created
(1 row(s) affected)
Msg 213, Level 16, State 1, Line 5
Column name or number of supplied values does not match table definition.
Msg 208, Level 16, State 1, Line 1
Invalid object name 'A'.
However, the rollback will be executed anyway even no error in transaction. In order to deal with this case, I use TRY ...CATCH as in 2.
2. use TRY ...CATCH
BEGIN TRY
begin transaction
create table A ...
insert into A values...
insert into A values... (error here!)
insert into A values...
--GO
END TRY
BEGIN CATCH
ROLLBACK
END CATCH
This time it doesn't allow statement GO here any more. S*o Batch is whole block between BEGIN TRY AND END TRY in this case?*
In addition, the result is not as I expected. The CREATE TABLE AND first insert were still executed and didn't roll back.
I searched again. It seems I need to SET XACT_ABORT ON in order to record these executed statement as uncommitted before touch commit. What I am understanding here is right? If so, I didn't add any commit statement in this case.
By the way, test are done on SQL SERVER 2012. Thanks for any clarification!
The reason it doesn't allow the GO statement is that the try and catch must be part of the same batch as metioned in this MSDN article. It states;
"Each TRY…CATCH construct must be inside a single batch, stored
procedure, or trigger. For example, you cannot place a TRY block in
one batch and the associated CATCH block in another batch. The
following script would generate an error:"
BEGIN TRY
SELECT *
FROM sys.messages
WHERE message_id = 21;
END TRY
GO
-- The previous GO breaks the script into two batches,
-- generating syntax errors. The script runs if this GO
-- is removed.
BEGIN CATCH
SELECT ERROR_NUMBER() AS ErrorNumber;
END CATCH;
GO
For an alternative idea on how to handle this, take a look at gbns answer on the question Nested stored procedures containing TRY CATCH ROLLBACK pattern? as he discusses his pattern/template for handling transactions including the use of (and reason for) XACT_ABORT and other nifty features. I'd also suggest reading the associated links in gbns answer
Aaron Betrand's answer to the same question refers to a Erland Somarsskog's article on error handling that is very similar to gbn's answer also.
Even though the title of the original question relates to nested transactions, it is still applicable in your situation I believe.
Try using SET XACT_ABORT ON in the begining of your script.
Check out XACT_ABORT MDSN reference page here

How to rollback an implicit SSMS transaction (statement with go at the end)?

Question:
Normally, you can undo a sql command with rollback.
BEGIN TRY
BEGIN TRANSACTION
/* run all your SQL statements */
COMMIT TRANSACTION
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION
END CATCH
My question now:
If 'one' did this
UPDATE TABLE_X SET FIELD_X = 'bla'
GO
in SSMS (notice the go at the end) and forgot to specify the WHERE clause,
is it possible (and how) to rollback the implicit transaction that SSMS executed this command in (statements with go at the end are implicitly executed in a transaction) ?
Note:
I didn't do that, but a colleague of mine did a few days ago (without go).
I undid the damage he did (fortunately I made a backup 0.5 hours before he did that), but for the future, it would be good to know this, because this happened to me once, too.
No, you can't, not easily. Restoring from backup is the best option.
see the link below, I think it will help you
How to recover the old data from table
thanks
Arun
GO does not specify the end of an implicit transaction, but the end of a batch. That's why you won't be able (unfortunately) to ROLLBACK your UPDATE after a GO.
From the MSDN page on GO:
GO is not a Transact-SQL statement; it is a command recognized by the
sqlcmd and osql utilities and SQL Server Management Studio Code
editor.
SQL Server utilities interpret GO as a signal that they should send
the current batch of Transact-SQL statements to an instance of SQL
Server. The current batch of statements is composed of all statements
entered since the last GO, or since the start of the ad hoc session or
script if this is the first GO.
The UPDATE command will only be seen as the start of an implicit transaction if you have specified SET IMPLICIT_TRANSACTIONS ON; (see here). In that case, a number of commands (CREATE, DELETE, UPDATE etcetera) will automatically start a new implicit transaction, and that transaction will not end until you issue a ROLLBACK or a COMMIT.
(See for more info on the difference between transactions and batches in SQL Server for example this question on ServerFault: SQL Server: Statements vs. Batches vs. Transactions vs. Connections.)

Auto update with script file with transaction

I need to provide an auto update feature to my application.
I am having problem in applying the SQL updates. I have the updated SQL statement in my .sql file and what i want to achieve is that if one statment fails then entire script file must be rolled back
Ex.
create procedure [dbo].[test1]
#P1 varchar(200),
#C1 int
as
begin
Select 1
end
GO
Insert into test (name) values ('vv')
Go
alter procedure [dbo].[test2]
#P1 varchar(200),
#C1 int
as
begin
Select 1
end
GO
Now in the above example, if i get the error in third statement of "alter procedure [dbo].[test2]" then i want to rollback the first two changes also which is creating SP of "test1" and inserting data into "test" table
How should i approach this task? Any help will be much appreciated.
If you need any more info then let me know
Normally, you would want to add a BEGIN TRAN at the beginning, remove the GO statements, and then handle the ROLLBACK TRAN/COMMIT TRAN with a TRY..CATCH block.
When dealing with DML though there are often statements that have to be at the start of a batch, so you can't wrap them in a TRY..CATCH block. In that case you need to put together a system that knows how to roll itself back.
A simple system would be just to backup the database at the start and restore it if anything fails (assuming that you are the only one accessing the database the whole time). Another method would be to log each batch that runs successfully and to have corresponding rollback scripts which you can run to put everything back should a later batch fail. This obviously requires much more work (writing an undo script for every script PLUS fully testing the rollbacks) and can also be a problem if people are still accessing the database while the upgrade is happening.
EDIT:
Here's an example of a simple TRY..CATCH block with transaction handling:
BEGIN TRY
BEGIN TRANSACTION
-- All of your code here, with `RAISERROR` used for any of your own error conditions
COMMIT TRANSACTION
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION
END CATCH
However, the TRY..CATCH block cannot span batches (maybe that's what I was thinking of when I said transactions couldn't), so in your case it would probably be something more like:
IF (OBJECT_ID('dbo.Error_Happened') IS NOT NULL)
DROP TABLE dbo.Error_Happened
GO
BEGIN TRANSACTION
<Some line of code>
IF (##ERROR <> 0)
CREATE TABLE dbo.Error_Happened (my_id INT)
IF (OBJECT_ID('dbo.Error_Happened') IS NOT NULL)
BEGIN
<Another line of code>
IF (##ERROR <> 0)
CREATE TABLE dbo.Error_Happened (my_id INT)
END
...
IF (OBJECT_ID('dbo.Error_Happened) IS NOT NULL)
BEGIN
ROLLBACK TRANSACTION
DROP TABLE dbo.Error_Happened
END
ELSE
COMMIT TRANSACTION
Unfortunately, because of the separate batches from the GO statements you can't use GOTO, you can't use the TRY..CATCH, and you can't persist a variable across the batches. This is why I used the very kludgy trick of creating a table to indicate an error.
A better way would be to simply have an error table and look for rows in it. Just keep in mind that your ROLLBACK will remove those rows at the end as well.

T-SQL 2005: combine multiple create/alter procedure calls in one transaction

I want to build a T-SQL change script that rolls out database changes from dev to test to production.
I've split the script into three parts:
DDL statements
changes for stored procedures (create and alter procedure)
data creation and modification
I want all of the changes in those three scripts to be made in a transaction. Either all changes in the script are processed or - upon an error - all changes are rolled back.
I managed to do this for the steps 1 and 3 by using the try/catch and begin transaction statements.
My problem is now to do the same thing for the stored procedures.
A call to "begin transaction" directly before a "create stored procedure" statement results in a syntax error telling me that "alter/create procedure statement must be the first statement inside a query batch".
So I wonder how I could combine multiple create/alter procedure statements in one transaction.
Any help is highly appreciated ;-)
Thanks
You can use dynamic SQL to create your stored procedures.
EXEC ('CREATE PROC dbo.foo AS ....`)
This will avoid the error "alter/create procedure statement must be the first statement inside a query batch"
Try this:
begin transaction
go
create procedure foo as begin select 1 end
go
commit transaction
try putting the steps in a job
BEGIN TRANSACTION
BEGIN TRY
-- Do your stuff here
COMMIT TRANSACTION
PRINT 'Successfull.'
END TRY
BEGIN CATCH
SELECT
ERROR_NUMBER() as ErrorNumber,
ERROR_MESSAGE() as ErrorMessage;
ROLLBACK TRANSACTION
END CATCH

What is the difference between ";" and "GO" in T-SQL?

I use ADO.NET as well as the sqlcmd utility to send SQL scripts to SQL Server 2008. What is the difference between using ; and GO to separate chunks of SQL?
GO is not actually a T-SQL command. The GO command was introduced by Microsoft tools as a way to separate batch statements such as the end of a stored procedure. GO is supported by the Microsoft SQL stack tools but is not formally part of other tools.
You cannot put a GO into a string of SQL and send it as part of a ADO.NET command object as SQL itself does not understand the term. Another way to demonstrate this is with the profiler: set up some statements that use GO in Query Analyzer/Management Studio and then run the profiler when you execute. You will see they are issued as separate commands to the server.
The semi-colon is used to signify the end of a statement itself, not necessarily a whole batch.
http://msdn.microsoft.com/en-us/library/ms188037.aspx
"GO" is similar to ; in many cases, but does in fact signify the end of a batch.
Each batch is committed when the "GO" statement is called, so if you have:
SELECT * FROM table-that-does-not-exist;
SELECT * FROM good-table;
in your batch, then the good-table select will never get called because the first select will cause an error.
If you instead had:
SELECT * FROM table-that-does-not-exist
GO
SELECT * FROM good-table
GO
The first select statement still causes an error, but since the second statement is in its own batch, it will still execute.
GO has nothing to do with committing a transaction.
semicolon is a statement separator. The previous statement(s) is not necessarily executed when a semicolon is encountered.
GO
Signifies the end of a batch. Executes the previous batch of statements, as does encountering the end of the block.
GO 2
Means execute the batch that many times. I think I've used that option maybe twice in my life. Then again, I'm not a DBA by trade.
Under SQL Server TSQL (2005 - 2016) bear in mind that:
Semicolon (;) is a block terminator.
GO is a batch terminator.
Additionally, GO can be used to invoke the same DML block multiple times using the following syntax:
GO [count]
Where [count] is a positive integer that indicates how many times the TSQL block of commands preceding said GO are to be carried out over and over.
Also, unlike semicolon, GO is mandatory before a new DDL, say, when you create a new view, since a semicolon separating previous commands will trigger an error. For example:
drop view #temporary_viewGO
create view #another_view...
--> NO ERRORS
If you replaced GO with a semicolon in the previous example, it will raise the following error message:
'CREATE VIEW' must be the first statement in a query batch.
'GO' is typically used to indicate the end of a batch of SQL statements which means that you could have a begin transaction and end transaction wrapped up into a single collection of statements that could fail or succeed together.
';' is generally used to separate multiple SQL statements from one another. This is noticable in SQL scripts that need to return multiple recordsets, such as `select * from table1; select * from table2;' which would result in two separate recordsets on the client's side.
The command GO means the end of a batch.
Therefore all variables declared before GO are invalid after the GO command.
Against the semicolon does not end the batch.
If You will use a DML command in a procedure, use the semicolon instead GO.
For example:
CREATE PROCEDURE SpMyProc
#myProcParam VARCHAR(20)
AS
DECLARE #myOtherParam INT = 5
;DISABLE TRIGGER ALL ON tMyTable
UPDATE tMyTable SET myVar = #myProcParam, mySecondVar = #myOtherParam
;ENABLE TRIGGER OLL ON tMyTable
I thought the ; character separates a list of SQL commands, GO just instructs SQL Server to commit all the previous commands.