user867198
user867198

Reputation: 1648

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

Upvotes: 1

Views: 506

Answers (1)

Tom H
Tom H

Reputation: 47464

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.

Upvotes: 1

Related Questions