Best practices to delete a set of tables in sql 2008 - sql-server

Basically I want to keep the transaction very simple but I should be able to rollback if any error occurs in the later part.
Something like:
BEGIN TRANSACTION
DELETE SET 1(this will delete first set of table)
COMMIT
DELETE SET 2 (will delete second set of table)
If any error occurs while deleting set 2 I should be able to rollback set 1 transaction as well. Let me know if we have any options to do like this. Appreciate for your help.

If any error occurs while deleting set
2 i should be able to rollback set 1
transaction as well.Let me know if we
have any options to do like this.
Appreciate for your help.
Then, why don't you do this?
BEGIN TRY
BEGIN TRANSACTION -- Start the transaction
DELETE SET 1(this will delete first set of table)
DELETE SET 2 (will delete second set of table)
-- If we reach here, success!
COMMIT
END TRY
BEGIN CATCH
-- Whoops, there was an error
IF ##TRANCOUNT > 0
ROLLBACK
-- Raise an error with the details of the exception
DECLARE #ErrMsg nvarchar(4000), #ErrSeverity int
SELECT #ErrMsg = ERROR_MESSAGE(),
#ErrSeverity = ERROR_SEVERITY()
RAISERROR(#ErrMsg, #ErrSeverity, 1)
END CATCH
Read here for a complete explanation.

If you mean deleting tables as in DELETE TABLE (I.e. the table, not the content), you are out of luck - DDL is not transacted.

Related

Commit transaction without begin transaction

I accidentally ran into a situation that I didn't put Begin Transaction at the beginning of my stored procedure and just wrote Commit Transaction as you can see below
ALTER PROCEDURE dbo.spTest
AS
BEGIN
DECLARE #MyId INT=1
BEGIN TRY
UPDATE Test
SET
-- Id -- this column value is auto-generated
CharName = 'david'
WHERE id=4
--Just to test locking behavior
WHILE(1=1)
BEGIN
SET #MyId=2;
END
COMMIT TRANSACTION
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION
END CATCH
END
I expected SQL Server to give me a run time error but it didn't happen. Of course I should mention that based on my test it didn't acquire any lock on the table due to the lack of Begin Transaction but what is the point of COMMIT TRANSACTION and ROLLBACK TRANSACTION in such a condition and why didn't SQL Server raise any error?
Edit:
if i remove while block and put WaitFor Sql raise error when reaches to COMMIT TRANSACTION
ALTER PROCEDURE dbo.spTest
AS
BEGIN
UPDATE Test
SET CharName = 'david'
WHERE id=4
PRINT 'waiting for a minute '
WAITFOR DELAY '00:00:10';
COMMIT TRANSACTION
END
Now i am receiving this error
The COMMIT TRANSACTION request has no corresponding BEGIN TRANSACTION
what is the point of COMMIT TRANSACTION and ROLLBACK TRANSACTION in such a condition?
There is no point in this case
and why didn't SQL Server raise any error?
I don't see any code that would raise an error. It would help if you could explain where and why you think an error should be raised
With regards to whatever you're actually doing here;
If the purpose of this proc is to hold a transaction open, you'd need something more like this:
ALTER PROCEDURE dbo.spTest
AS
BEGIN
BEGIN TRANSACTION
UPDATE Test
SET CharName = 'david'
WHERE id=4
--Endless loop
WHILE(1=1)
BEGIN
PRINT 'waiting for a minute inside a transaction. Try something from another session'
WAITFOR DELAY '00:01';
END
-- Transaction will actually never be committed
-- Because this line will never be reached
-- because it's preceded by an endless loop
COMMIT TRANSACTION
END
The TRY / CATCH is a bit of a distraction. I've removed it.

SQL Error Handling Help: While loop with tran with try catch

Sorry for the horrid title...it's not very easy to explain what I'm asking in one line.
I want to run two dependent DML queries in a loop...if either of the queries fail/throw an error...I want to rollback the transaction, exit the loop, and terminate the entire proc while throwing an error (so jobs will detect the failure). I think what I have is correct...but I have a few questions because I want to better understand how it works. I've read the Microsoft documentation...but I'm still unclear on some things.
I know by using SET XACT_ABORT ON; that it will handle the rollback for the tran. Does that mean I do not need to check for IF (##TRANCOUNT > 0) in the CATCH block?
The other question...the reason I have the TRY...CATCH block is because of the WHILE loop...I'm not sure if the failed transaction will also terminate the proc, so I'm forcing it with THROW?
Here's what I have: (Ignore the fact that it's an infinite loop, I'm not including the break logic to keep the example simple)
SET XACT_ABORT ON;
WHILE(1=1)
BEGIN
BEGIN TRY
BEGIN TRAN;
--DML Query 1
--DML Query 2
COMMIT TRAN;
END TRY
BEGIN CATCH
IF (##TRANCOUNT > 0)
ROLLBACK TRAN;
THROW;
END CATCH
END
UPDATE
Okay I'm trying to figure out how to test it myself, I was having a hard time figuring out how to test it, but I think I've got it now. So now I feel bad for posting the question :D
Here's the test I've got so far...I'll update it as I make changes to it. It appears that the IF (##TRANCOUNT > 0) in the CATCH block is not necessary, because when I remove the ROLLBACK and check for a transaction after a failure...there's no open transaction. However, if I leave it in...the IF statement resolves to true and it still runs the rollback without error.
SET NOCOUNT ON;
IF OBJECT_ID('dbo.ChadTestTable1') IS NOT NULL DROP TABLE dbo.ChadTestTable1; --SELECT * FROM dbo.ChadTestTable1
CREATE TABLE dbo.ChadTestTable1 (TestField VARCHAR(10) NOT NULL)
IF OBJECT_ID('dbo.ChadTestTable2') IS NOT NULL DROP TABLE dbo.ChadTestTable2; --SELECT * FROM dbo.ChadTestTable2
CREATE TABLE dbo.ChadTestTable2 (TestField VARCHAR(10) NOT NULL)
INSERT INTO dbo.ChadTestTable1 (TestField) VALUES ('Test1')
INSERT INTO dbo.ChadTestTable2 (TestField) VALUES ('Test1')
SET XACT_ABORT ON;
WHILE(1=1)
BEGIN
BEGIN TRY
BEGIN TRAN;
RAISERROR('Update first table',0,1) WITH NOWAIT;
UPDATE dbo.ChadTestTable1 SET TestField = 'Test3'
RAISERROR('Update second table',0,1) WITH NOWAIT;
UPDATE dbo.ChadTestTable2 SET TestField = NULL
RAISERROR('Updates done',0,1) WITH NOWAIT;
COMMIT TRAN;
END TRY
BEGIN CATCH
--It appears this isn't necessary...but if it's here, it still resolves to true and runs?
IF (##TRANCOUNT > 0)
BEGIN
RAISERROR('Rolling back transaction',0,1) WITH NOWAIT;
ROLLBACK TRAN;
END
RAISERROR('Throwing Error',0,1) WITH NOWAIT;
THROW;
END CATCH
RAISERROR('End of loop',0,1) WITH NOWAIT;
BREAK;
END
SELECT * FROM dbo.ChadTestTable1 ctt
SELECT * FROM dbo.ChadTestTable2 ctt
So I figured out my own answer. I was having a fairly difficult time trying to figure out how to test this myself which is why I posted up the question, but I eventually figured it out. Here's what I found...90% of it you can probably find in Microsoft Documentation, but I read that, and was still left confused, so I resulted to bruteforce testing.
Here's what I found that I was missing after reading the documentation:
XACT_ABORT ON - Not only does it rollback the transaction, but it will also throw a terminating error...aka, if it's on and a transaction is in a loop, and an error is thrown in the tran, it will rollback the tran, break the loop, and it will also end the proc and nothing after it will be run. The proc/script ends there.
Therefore...I don't need the TRY...CATCH block in order to run the THROW and I don't need to rollback because it will be done automatically. I'm happy to have found this because that simplifies my code quite a bit.

SQL Server XACT_ABORT with exclusion

I have a larger stored procedure which utilizes several TRY/CATCH blocks in order to catch and log individual errors. I have also wrapped a transaction around the entire contents of the procedure, so as to be able to roll back the entire thing in the event of an error raised somewhere along the way (in order to prevent a lot of messy cleanup); XACT_ABORT has been enabled since it would otherwise not roll back the entire transaction.
Key component:
There is a table in my database which gets a record inserted each time this procedure is run with the results of operations and details on what went wrong.
Funny thing is happening - actually, when I finally figured out what was wrong, it was pretty obvious... the the insert statement into my log table is getting rolled back as well, hence, if I am not running this out of SSMS, I will not be able to see that this was even run, as the rollback removes all trances of activity.
Question:
Would it be possible to have the entire transaction roll back with the exception of this single insert statement? I would still want to preserve the error message which I compile during the running of the stored procedure.
Thanks so much!
~Eli
Update 6/28
Here's a code sample of what I'm looking at. Key difference between this and the samples posed by #Alex and #gameiswar is that in my case, the try/catch blocks are all nested inside the single transaction. The purpose of this is to have multiple catches (for the multiple tables), though we would the entire mess to be rolled back even if the last update failed.
SET XACT_ABORT ON;
BEGIN TRANSACTION
DECLARE #message AS VARCHAR(MAX) = '';
-- TABLE 1
BEGIN TRY
UPDATE TABLE xx
SET yy = zz
END TRY
BEGIN CATCH
SET #message = 'TABLE 1 '+ ERROR_MESSAGE();
INSERT INTO LOGTABLE
SELECT
GETDATE(),
#message
RETURN;
END CATCH
-- TABLE 2
BEGIN TRY
UPDATE TABLE sss
SET tt = xyz
END TRY
BEGIN CATCH
SET #message = 'TABLE 2 '+ ERROR_MESSAGE();
INSERT INTO LOGTABLE
SELECT
GETDATE(),
#message
RETURN;
END CATCH
COMMIT TRANSACTION
You can try something like below ,which ensures you log the operation.This takes advantage of the fact that table variables dont get rollbacked..
Psuedo code only to give you idea:
create table test1
(
id int primary key
)
create table logg
(
errmsg varchar(max)
)
declare #errmsg varchar(max)
set xact_abort on
begin try
begin tran
insert into test1
select 1
insert into test1
select 1
commit
end try
begin catch
set #errmsg=ERROR_MESSAGE()
select #errmsg as "in block"
if ##trancount>0
rollback tran
end catch
set xact_abort off
select #errmsg as "after block";
insert into logg
select #errmsg
select * from logg
OK... I was able to solve this using a combination of the great suggestions put forth by Alex and GameisWar, with the addition of the T-SQL GOTO control flow statement.
The basic ideas was to store the error message in a variable, which survives a rollback, then have the Catch send you to a FAILURE label which will do the following:
Rollback the transaction
Insert a record into the log table, using the data from the aforementioned variable
Exit the stored procedure
I also use a second GOTO statement to make sure that a successful run will skip over the FAILURE section and commit the transaction.
Below is a code snippet of what the test SQL looked like. It worked like a charm, and I have already implemented this and tested it (successfully) in our production environment.
I really appreciate all the help and input!
SET XACT_ABORT ON
DECLARE #MESSAGE VARCHAR(MAX) = '';
BEGIN TRANSACTION
BEGIN TRY
INSERT INTO TEST_TABLE VALUES ('TEST'); -- WORKS FINE
END TRY
BEGIN CATCH
SET #MESSAGE = 'ERROR - SECTION 1: ' + ERROR_MESSAGE();
GOTO FAILURE;
END CATCH
BEGIN TRY
INSERT INTO TEST_TABLE VALUES ('TEST2'); --WORKS FINE
INSERT INTO TEST_TABLE VALUES ('ANOTHER TEST'); -- ERRORS OUT, DATA WOULD BE TRUNCATED
END TRY
BEGIN CATCH
SET #MESSAGE = 'ERROR - SECTION 2: ' + ERROR_MESSAGE();
GOTO FAILURE;
END CATCH
GOTO SUCCESS;
FAILURE:
ROLLBACK
INSERT INTO LOGG SELECT #MESSAGE
RETURN;
SUCCESS:
COMMIT TRANSACTION
I don't know details but IMHO general logic can be like this.
--set XACT_ABORT ON --not include it
declare #result varchar(max) --collect details in case you need it
begin transaction
begin try
--your logic here
--if something wrong RAISERROR(...#result)
--everything OK
commit
end try
begin catch
--collect error_message() and other into #result
rollback
end catch
insert log(result) values (#result)

Multiple Roll back for single Transcation

I have to update multiple number of rows in table.
My requirement is, If for any reason , the update result returns 0, then the entire transaction should be rolled back.
At the same time if there is any exception occurred, then also the complete transaction must be rolled back.
In short I need to roll back the entire update transaction either if update statement returns 0 or if any exception has been occurred while updating the table.
This is the code I used.
CREATE TYPE [DBO].[EMPLOYEETYPETABLETYPE] AS TABLE
( EmployeeStatusKey INT, EmployeeStatusName VARCHAR(50) )
CREATE PROCEDURE [dbo].[usp_UpdateEmployeeStatusType]
#EmploymentStatusDetails [DBO].[EMPLOYEETYPETABLETYPE] READONLY
AS
BEGIN
SET NOCOUNT ON;
DECLARE #TransactionName varchar(20) = 'UpdateEmployeeStatus';
DECLARE #rowcount1 INT
BEGIN
BEGIN TRY
BEGIN TRANSACTION #TransactionName
UPDATE ES1
SET
ES1.EmployeeStatusName=ES2.EmployeeStatusName
FROM
[dbo].[EmployeeStatusTypes] ES1
INNER JOIN
#EmploymentStatusDetails ES2
ON
ES1.EmployeeStatusKey= ES2.EmployeeStatusKey
SET
#ROWCOUNT1=##ROWCOUNT
IF #rowcount1 =0
GOTO PROBLEM
PROBLEM:
ROLLBACK TRAN #TransactionName
COMMIT
END TRY
BEGIN CATCH
SET #ROWCOUNT1=0
ROLLBACK TRAN #TransactionName
END CATCH
IF #rowcount1 =0
SELECT -178,#rowcount1;
ELSE
SELECT 178,#rowcount1;
END
END
I am passing a datatable to the stored procedure from the C# code.
When I execute the Sp, No error is thrown But When I call it from the C# code I am getting the Exception
Exception: The ROLLBACK TRANSACTION request has no corresponding BEGIN TRANSACTION.
Please help and Thanks in advance....
Remove that awful GOTO
IF #rowcount1 =0
ROLLBACK TRAN #TransactionName
ELSE
COMMIT TRAN #TransactionName
And review TRY-CATCH documentation. You have to check whether there is any transaction to commit or rollback by checking XACT_STATE() value.
I tested your script with simplified one:
BEGIN TRY
print 'A:' + cast(##trancount as varchar)
BEGIN TRANSACTION tranName
print 'B:' + cast(##trancount as varchar)
GOTO PROBLEM -- you can comment this to simulate there is no error
PROBLEM:
begin
ROLLBACK TRAN tranName
print 'there is a problem'
end
print 'C:' + cast(##trancount as varchar)
COMMIT
END TRY
BEGIN CATCH
print 'D:' + cast(##trancount as varchar)
ROLLBACK TRAN tranName
END CATCH
when there is a problem, output is like
A:0
B:1
there is a problem
C:0
D:0
Msg 3903, Level 16, State 1, Line 18
The ROLLBACK TRANSACTION request has no corresponding BEGIN TRANSACTION.
if I comment the GOTO PROBLEM so there is not a problem, output is like this:
A:0
B:1
there is a problem
C:0
D:0
Msg 3903, Level 16, State 1, Line 18
The ROLLBACK TRANSACTION request has no corresponding BEGIN TRANSACTION.
you see that the section under GOTO, still is executing.
finally, the COMMIT still happens where there is not transaction available, so COMMIT throws and error. This means your script throws an exception.
If you get rid of the GOTO, you'll be good. using GOTO is a bad practice anyway.

Exit and rollback everything in script on error

I have a TSQL script that does a lot of database structure adjustments but it's not really safe to just let it go through when something fails.
to make things clear:
using MS SQL 2005
it's NOT a stored procedure, just a script file (.sql)
what I have is something in the following order
BEGIN TRANSACTION
ALTER Stuff
GO
CREATE New Stuff
GO
DROP Old Stuff
GO
IF ##ERROR != 0
BEGIN
PRINT 'Errors Found ... Rolling back'
ROLLBACK TRANSACTION
RETURN
END
ELSE
PRINT 'No Errors ... Committing changes'
COMMIT TRANSACTION
just to illustrate what I'm working with ... can't go into specifics
now, the problem ...
When I introduce an error (to test if things get rolled back), I get a statement that the ROLLBACK TRANSACTION could not find a corresponding BEGIN TRANSACTION.
This leads me to believe that something when REALLY wrong and the transaction was already killed.
what I also noticed is that the script didn't fully quit on error and thus DID try to execute every statement after the error occured. (I noticed this when new tables showed up when I wasn't expecting them because it should have rollbacked)
When the error occurs, the transaction is rolled back automatically, and the current batch is aborted.
Execution continues into the next batch, however. So all the stuff in the batches after the error gets executed. And then when you check for errors later, you try to rollback an already rolled back transaction.
Also, to stop the entire script, not just the current batch, you should use:
raiserror('Error description here', 20, -1) with log
See my answer here for details on that one.
So you need to check for #error after each batch, I think something like this should work:
BEGIN TRANSACTION
GO
ALTER Stuff
GO
if ##error != 0 raiserror('Script failed', 20, -1) with log
GO
CREATE New Stuff
GO
if ##error != 0 raiserror('Script failed', 20, -1) with log
GO
DROP Old Stuff
GO
if ##error != 0 raiserror('Script failed', 20, -1) with log
GO
PRINT 'No Errors ... Committing changes'
COMMIT TRANSACTION
Try using RETURN. this will exit the script or procedure immediately and will not execute any of the following statements. You can use this in conjunction with BEGIN, ROLLBACK and COMMIT TRANSACTION statements to undo any data damage:
BEGIN
BEGIN TRANSACTION
<first batch>
IF ##error <> 0
begin
RAISERROR ('first batch failed',16,-1)
ROLLBACK TRANSACTION
RETURN
end
<second batch>
IF ##error <> 0
begin
RAISERROR ('second batch failed',16,-1)
ROLLBACK TRANSACTION
RETURN
end
PRINT 'WIN!'
COMMIT TRANSACTION
END
I didn't use the raiseerror solution, because it failed as I didn't have admin permissions. I extended the noexec on/off solution with the transaction handling as follows:
set noexec off
begin transaction
go
<First batch, do something here>
go
if ##error != 0 set noexec on;
<Second batch, do something here>
go
if ##error != 0 set noexec on;
<... etc>
declare #finished bit;
set #finished = 1;
SET noexec off;
IF #finished = 1
BEGIN
PRINT 'Committing changes'
COMMIT TRANSACTION
END
ELSE
BEGIN
PRINT 'Errors occured. Rolling back changes'
ROLLBACK TRANSACTION
END
Apparently the compiler "understands" the #finished variable in the IF, even if there was an error and the execution was disabled. However, the value is set to 1 only if the execution was not disabled. Hence I can nicely commit or rollback the transaction accordingly.
You could try something like this... If you are using Try block... The error level 16, (or most of application error), immediately transfers the control to the CATCH block without executing any further statements in the try block...
Begin Transaction
Begin Try
-- Do your Stuff
If (##RowCount <> 1) -- Error condition
Begin
Raiserror('Error Message',16,1)
End
Commit
End Try
Begin Catch
IF ##Trancount > 0
begin
Rollback Transaction
End
Declare #ErrMsg varchar(4000), #Errseverity int
SELECT #ErrMsg = ERROR_MESSAGE(),
#ErrSeverity = ERROR_SEVERITY()
RAISERROR(#ErrMsg, #ErrSeverity, 1)
End Catch
Hope this helps...
SET XACT_ABORT ON
BEGIN TRAN
-- Batch 1
GO
if ##TRANCOUNT = 0
SET NOEXEC ON;
GO
-- Batch 2
GO
if ##TRANCOUNT = 0
SET NOEXEC ON;
GO
-- Batch 3
GO
if ##TRANCOUNT > 0
COMMIT
GO

Resources