I have written a transaction like:
BEGIN TRAN
UPDATE [Table1]
SET [Name] = 'abcd'
WHERE [ID] = 1
UPDATE [Table2]
SET [Product] = 'efgh'
WHERE [ID] = 10
UPDATE [Table3]
SET [Customar] = 'ijkl'
WHERE [ID] = 11
Now I want to rollback if any UPDATE is not success. For example in Table2 if there is no Product with ID=10 the transaction should be rolled back. How to do this?Please note that I am using SQLServer 2000.
SQL Server 2000. You don't need rollback if you use SET XACT_ABORT ON
SET XACT_ABORT ON --to ensure rollback
BEGIN TRAN -- ##TRANCOUNT + 1
UPDATE [Table1]
SET [Name] = 'abcd'
WHERE [ID] = 1
IF ##ROWCOUNT = 0 ROLLBACK TRAN
IF ##TRANCOUNT > 0
BEGIN
UPDATE [Table2]
SET [Product] = 'efgh'
WHERE [ID] = 10
IF ##ROWCOUNT = 0 ROLLBACK TRAN
END
IF ##TRANCOUNT > 0
BEGIN
UPDATE [Table3]
SET [Customar] = 'ijkl'
WHERE [ID] = 11
IF ##ROWCOUNT = 0 ROLLBACK TRAN
END
IF ##TRANCOUNT > 0 COMMIT TRAN
Before each UPDATE statement you need to do BEGIN TRAN and after each of your UPDATE statement, you need to do this -
if ##Error > 0
THEN
BEGIN
ROLLBACK TRAN
END
Else
BEGIN
COMMIT TRAN
END
Related
When the internal SP tries to rollback transaction it completed with an error:
Msg 266, Level 16, State 2, Procedure ptest, Line 0 [Batch Start Line
37] Transaction count after EXECUTE indicates a mismatching number of
BEGIN and COMMIT statements. Previous count = 1, current count = 0.
Is it possible to rollback transaction inside the internal SP?
IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[ptest]') AND type in (N'P', N'PC'))
BEGIN
EXEC dbo.sp_executesql #statement = N'CREATE PROCEDURE [dbo].[ptest] AS'
END
GRANT EXECUTE on [dbo].[ptest] to public;
GO
ALTER PROCEDURE [dbo].[ptest]
#parrollback bit = 0
AS
BEGIN
SET NOCOUNT ON
SET XACT_ABORT OFF
select ##TRANCOUNT as '##TRANCOUNT:[ptest] '
if #parrollback is not null and #parrollback>0
if ##TRANCOUNT>0 rollback tran;
END
GO
IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[pcaller]') AND type in (N'P', N'PC'))
BEGIN
EXEC dbo.sp_executesql #statement = N'CREATE PROCEDURE [dbo].[pcaller] AS'
END
GRANT EXECUTE on [dbo].[pcaller] to public;
GO
ALTER PROCEDURE [dbo].[pcaller]
AS
BEGIN
SET NOCOUNT ON
begin tran
select ##TRANCOUNT as '##TRANCOUNT: before [ptest]'
exec ptest 1
select ##TRANCOUNT as '##TRANCOUNT: after [ptest] '
if ##TRANCOUNT>0 rollback tran;
END
GO
-------------
exec pcaller
/*
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[ptest]') AND type in (N'P', N'PC'))
drop proc pcaller
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[ptest]') AND type in (N'P', N'PC'))
drop proc ptest
*/
try not to handle parent transactions inside a child procedure (exception when XACT_STATE() = -1). Handle the transaction at the "execution" level that started it.
if a procedure is executed in a parent transaction, then create a savepoint and rollback to it when needed. capture the execution result of the child procedure and handle the transaction at the parent level (if the parent is the one that begun the transaction).
CREATE OR ALTER PROCEDURE [dbo].[ptest] #parrollback bit = 0
AS
BEGIN
SET NOCOUNT ON
SET XACT_ABORT OFF
DECLARE #trancount INT = ##TRANCOUNT;
IF #trancount = 0
BEGIN
BEGIN TRANSACTION;
END
ELSE
BEGIN
SAVE TRANSACTION MySavepoint;
END
--do stuff.........
--when it is time to commit or check for errors
--assume #parrollback is the main control criterium
IF #parrollback = 1
BEGIN
IF #trancount = 0
BEGIN
ROLLBACK TRANSACTION;
RETURN(0);
END
ELSE
BEGIN
ROLLBACK TRANSACTION MySavePoint
RETURN (1);
END
END
--just handle #parrollback <> 1, for completeness of the test
IF #trancount = 0
BEGIN
COMMIT TRANSACTION;
END
RETURN (0);
END
GO
CREATE OR ALTER PROCEDURE dbo.pcaller
AS
BEGIN
SET NOCOUNT ON
DECLARE #ptestexec INT;
BEGIN TRANSACTION
select ##TRANCOUNT as '##TRANCOUNT: before [ptest]'
EXEC #ptestexec = dbo.ptest #parrollback = 1;
IF #ptestexec = 1
BEGIN
ROLLBACK TRANSACTION
END
ELSE
BEGIN
COMMIT TRANSACTION
END
--execute ptest, outside of a transaction
EXEC #ptestexec = dbo.ptest #parrollback = 0;
SELECT ##TRANCOUNT AS trancount1;
EXEC #ptestexec = dbo.ptest #parrollback = 1;
SELECT ##TRANCOUNT AS trancount2;
--execute ptest, outside of a transaction
BEGIN TRANSACTION;
--ptest executed in a parent transaction
EXEC #ptestexec = dbo.ptest #parrollback = 0;
SELECT ##TRANCOUNT AS trancount3; --ptest does not affect the parent transactions
COMMIT TRANSACTION --or rollback
END
GO
EXEC dbo.pcaller
GO
I writed this trigger:
ALTER TRIGGER [dbo].[trg_abort_insert]
ON [dbo].[F_DOCCURRENTPIECE]
AFTER UPDATE
AS
BEGIN
SET NOCOUNT ON
SET XACT_ABORT ON
BEGIN TRY
BEGIN TRAN
IF EXISTS (SELECT *
FROM Inserted i
INNER JOIN Deleted d ON i.CBMARQ= d.CBMARQ
WHERE i.DC_Piece <> d.DC_Piece
AND i.DC_Domaine = 0
AND i.DC_IdCol = 6)
COMMIT TRAN
END TRY
BEGIN CATCH
SELECT ERROR_MESSAGE()
IF ##TRANCOUNT>0
ROLLBACK
END CATCH;
END
I'am having this error:
Transaction count after EXECUTE indicates a mismatching number of BEGIN and COMMIT statements. Previous count = 1, current count = 2
Pleaze help me
This is because you have nested transaction. I mean a transaction was open when trigger was fired. You need to modify your trigger to handle nested transaction like mentioned below :
create TRIGGER [dbo].[trg_abort_insert]
ON [dbo].[F_DOCCURRENTPIECE]
AFTER UPDATE
AS
BEGIN
SET NOCOUNT ON
SET XACT_ABORT ON
declare #trancount int = ##trancount
BEGIN TRY
if #trancount > 0
begin
save transaction t1
end
else
begin
begin transaction
end
IF EXISTS (SELECT *
FROM Inserted i
INNER JOIN Deleted d ON i.CBMARQ= d.CBMARQ
WHERE i.DC_Piece <> d.DC_Piece
AND i.DC_Domaine = 0
AND i.DC_IdCol = 6)
if #trancount = 0
begin
COMMIT TRAN
end
END TRY
BEGIN CATCH
if #trancount > 0
rollback transaction t1
if #trancount = 0
rollback transaction
SELECT ERROR_MESSAGE()
END CATCH;
end
This should work for you. Let me know if this helps.
I encountered an error while trying to execute the query below.
if exists (select null from sys.sysobjects where type='P' and name = 'myProc')
drop PROCEDURE myProc
go
create procedure myProc
as
begin
set nocount on
set xact_abort on
begin try
declare #trancount int = ##trancount
if #trancount = 0
begin tran
else
save tran MySave
raiserror ('123213123',16,1)
if #trancount = 0
commit
end try
begin catch
if #trancount = 0
rollback
else
if XACT_STATE() = 1
rollback tran MySave
else
rollback
end catch
end
go
begin tran
EXEC myProc
if ##TRANCOUNT >0
rollback
the error is
Transaction count after EXECUTE indicates a mismatching number of BEGIN and COMMIT statements. Previous count = 1, current count = 0.
I've read many topics about similar problems but can't get it clear so far what's the reason in my case.
Could anyone explain me why I get it and what should I do to avoid it.
Thanks in advance
upd. I can simplify the code of MyProc like
create procedure myProc
as
begin
set nocount on
set xact_abort on
begin try
begin tran
raiserror ('123213123',16,1)
commit
end try
begin catch
rollback
end catch
end
go
It doesn't solve my problems. the same error occurs
Try this:
ALTER PROCEDURE myProc
AS
BEGIN
SET NOCOUNT ON
SET XACT_ABORT ON
BEGIN TRY
DECLARE #trancount INT = ##trancount
IF #trancount = 0
BEGIN TRAN
ELSE
SAVE TRAN MySave
RAISERROR ('123213123',16,1)
IF #trancount = 0
COMMIT
END TRY
BEGIN CATCH
IF XACT_STATE() <> 0
AND #trancount = 0
ROLLBACK TRANSACTION;
END CATCH
END
GO
BEGIN TRAN
EXEC myProc
IF ##TRANCOUNT > 0
ROLLBACK
I have 8 tables . One parent and 7 children . Inside the while loop and delete from table one by one.
If any error during the loop all transaction rollback . Is it possible inside the while loop.
Example :
declare #Count int, #intFlag int
begin try
set #Count = (select count(ID) from MyTable where [Date] between getdate()-1 and getdate())
if #Count > 0
begin
set #intFlag = 1
begin transaction
while (#intFlag <= #Count)
begin
update MyTable1
set column1 = 1
where [Date] between getdate()-1 and getdate();
update MyTable2
set column2 = 1
where [Date] between getdate()-1 and getdate();
set #intFlag = #intFlag + 1
end;
commit
end
end try
begin catch
if ##trancount > 0 rollback
end catch
If any error during the processes its roll back all child table transaction
try catch block above the loop like following
BEGIN TRANSACTION
BEGIN TRY
/*
* YOUR LOOP
*/
COMMIT TRANSACTION
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION
END CATCH
I read this Stackoverflow question
Nested stored procedures containing TRY CATCH ROLLBACK pattern?
I'm in need of clarification on Transaction template what gbn have answered.
I couldn't comment and ask there.
CREATE PROCEDURE [Name]
AS
SET XACT_ABORT, NOCOUNT ON
DECLARE #starttrancount int
BEGIN TRY
SELECT #starttrancount = ##TRANCOUNT
IF #starttrancount = 0
BEGIN TRANSACTION
[...Perform work, call nested procedures...]
IF #starttrancount = 0
COMMIT TRANSACTION
END TRY
BEGIN CATCH
IF XACT_STATE() <> 0 AND #starttrancount = 0
ROLLBACK TRANSACTION
RAISERROR [rethrow caught error using #ErrorNumber, #ErrorMessage, etc]
END CATCH
GO
My Question is!
Why to use ?
SELECT #starttrancount = ##TRANCOUNT , rather than using ##TRANCOUNT directly??
and why to check this?
IF #starttrancount = 0
BEGIN TRANSACTION
IF #starttrancount = 0
COMMIT TRANSACTION
I'm new to transaction , explanation with example would be so helpfull.
Thanks :)
IF #starttrancount = 0 BEGIN TRANSACTION
IF #starttrancount = 0 COMMIT TRANSACTION
These are used because, the #starttrancount guaranteed to let only the outer most stored procedure to use transaction, so that only one transaction exists.
Example: We want to execute outer most procedure, then the transaction must be used in outer most procedure only.
Outer Stored Procedure
CREATE PROCEDURE sp_outer
AS
SET XACT_ABORT, NOCOUNT ON
DECLARE #starttrancount int
BEGIN TRY
SELECT #starttrancount = ##TRANCOUNT -- Initially ##TRANSCOUNT =0
IF #starttrancount = 0
BEGIN TRANSACTION -- ##TRANSCOUNT =1
EXEC sp_inner -- Inner Procedure is called with ##TRANSCOUNT =1
-- so that Transaction in inner procedure will not be used.
-- Per Transaction is exists.
IF #starttrancount = 0
COMMIT TRANSACTION -- ##TRANSCOUNT = 0
END TRY
BEGIN CATCH
IF XACT_STATE() <> 0 AND #starttrancount = 0
ROLLBACK TRANSACTION -- If Error occurs Rollback takes place.
RAISERROR [rethrow caught error using #ErrorNumber, #ErrorMessage, etc]
END CATCH
GO
2.Inner Stored Procedure
CREATE PROCEDURE sp_inner
AS
SET XACT_ABORT, NOCOUNT ON
DECLARE #starttrancount int
BEGIN TRY
SELECT #starttrancount = ##TRANCOUNT -- ##TRANCOUNT =1
IF #starttrancount = 0
BEGIN TRANSACTION -- Skipped
[...Perform work, call nested procedures...]
IF #starttrancount = 0
COMMIT TRANSACTION -- Skipped
END TRY
BEGIN CATCH
IF XACT_STATE() <> 0 AND #starttrancount = 0
ROLLBACK TRANSACTION -- if Error Caught Roll back does not happen here
RAISERROR [rethrow caught error using #ErrorNumber, #ErrorMessage, etc] -- Error thrown to outer stored procedure.
END CATCH
GO
why SELECT #starttrancount = ##TRANCOUNT , rather than using
##TRANCOUNT directly??
Since ##TRANSCOUNT scope exists in both the stored procedures, for maintaining values within the procedure's scope
#starttrancount variable is used.