I have code like below, now I need to commit only inner transactions and outer transaction may commit or roll-back. How to handle it?
BEGIN TRY
BEGIN TRANSACTION
INSERT TABLE T1 (1,2,3)
----
----
----
IF t1 > 10
BEGIN
BEGIN TRANSACTION
INSERT INTO ERROR_LOG (XX)
COMMIT
return 1
END
COMMIT
END TRY
BEGIN CATCH
ROLLBACK
END CATCH
Only when t1 > 10 then transaction should commit the error log and terminates program
If you're hoping to store some results into ERROR_LOG and rollback other changes, the inner transaction is not going to do that, since in SQL Server the outer transaction will rollback everything.
Here's some testing & explanation done by Paul Randal: http://www.sqlskills.com/blogs/paul/a-sql-server-dba-myth-a-day-2630-nested-transactions-are-real/
One way to get around of this limitation is to use table variable for the logging, since it will not be rolled back, and then insert the results into the log after doing the rollback.
Related
I have a transaction and two tables where i am inserting some data, can I do partial commits in SQL server
BEGIN TRANSACTION tran1
BEGIN TRY
--Insert into Table1
--Insert into Table2
COMMIT TRANSACTION tran1
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION tran1
END CATCH
Above code will rollback both tables data, is there a way we can commit table 1 if there is no error on table 1 insert but rollback table 2 if there is any error occurred.
The answer is yes, although transactions are indeed atomic you could use a savepoint. So in your case, the code could look like this (untested):
BEGIN TRY
BEGIN TRANSACTION
--Insert into Table1
-- savepoint
SAVE TRANSACTION tran1
--Insert into Table2
-- commit the whole transaction
COMMIT TRANSACTION
END TRY
BEGIN CATCH
-- rollback to savepoint
IF ##TRANCOUNT > 0 ROLLBACK TRANSACTION tran1
END CATCH
You may have to adjust insertion order.
Nope, you can't.
A transaction is atomic. That is all of the steps are performed as one, commited together, or rolled back.
If you put both inserts into a single transaction, you can't commit to one table.
You can use different transactions if possible.
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.
I created an adhoc which has lots of delete/insert action.
For data integrity, I would like to rollback all if any action goes wrong.
The following is the pseudo code:
BEGIN TRY
BEGIN TRANSACTION
INSERT INTO New_Table VALUES(......)
DELETE FROM Old_Table
COMMIT
END TRY
BEGIN CATCH
IF ##TRANCOUNT > 0
ROLLBACK
END CATCH
Is there any performance issue here? I will use this logic about 10 times for insert/delete data in this adhoc.
I am seeing some strange behavior which has occurred on random basis.
Here's what my store procedure basically does.
begin try
begin tran
insert into table1
update table2
insert into table3
commit tran
end try
begin catch
rollback tran
end catch
For most of the time above code works fine except once in a while(once per day or two) when some error occurs, the transaction does not rollback the changes from all 3 tables.
begin try
begin tran
insert into table1----Rollback doesn't happen
update table2--Rollback happens
insert into table3--Rollback happens
commit tran
end try
begin catch
rollback tran
end catch
Can anyone please can suggest something where I might be wrong or do I need to handle transaction in some another way?
Thanks in advance.
Check your SET XACT_ABORT setting and what the error level inside the failing sql actually is
How to make SET XACT_ABORT ON rollback the transaction?
--Drop Table Tab1
Begin Transaction TR1;
Save Transaction TR1;
Create Table Tab1(f1 decimal(10,0));
Begin Transaction TR2
Save Transaction TR2
insert into Tab1 values(1);
Begin Transaction TR3;
Save Transaction TR3;
insert into Tab1 values(2);
Begin Try
insert into Tab1 values('OK');
Commit Transaction TR3;
END TRY
BEGIN Catch
print 'catch'
RollBack Transaction TR3;
End Catch
insert into Tab1 values(3);
Commit Transaction TR2
insert into Tab1 values(4);
Commit Transaction TR1;
--Commit Transaction;
select * from Tab1;
Drop Table Tab1
Select ##TRANCount
Error Occures :
Msg 3931, Level 16, State 1, Line 17
The current transaction cannot be committed and cannot be rolled back to a savepoint. Roll back the entire transaction.
How to handle this.
When certain type of errors get raised you cannot rollback to a save point. See Martin Smith's answer to Rollback transaction to savepoint on failing ALTER TABLE … ADD CONSTRAINT. The way you detect this is to test Xact_state().
However your problem is somewhat different because you're also trying to use nested transactions. Nested transactions don't really work in SQL as we would expect them to.
You can only name the outermost transaction. See Transactions (Database Engine)
For example this fails with Cannot roll back TR2. No transaction or savepoint of that name was found.
BEGIN TRANSACTION TR1;
BEGIN TRANSACTION TR2
ROLLBACK TRANSACTION TR2
COMMIT Transaction TR1
From Nesting Transactions
Committing inner transactions is ignored by the SQL Server Database Engine
It is not legal for the transaction_name parameter of a ROLLBACK TRANSACTION statement to refer to the inner transactions of a set of named nested transactions. transaction_name can refer only to the transaction name of the outermost transaction
Paul S. Randal explores this further in A SQL Server DBA myth a day: (26/30) nested transactions are real
The best you can do is use Save points instead and check the Xact_state in your catch and at the end.
BEGIN TRANSACTION tr1;
SAVE TRANSACTION tr2;
CREATE TABLE tab1
(
f1 DECIMAL(10, 0)
);
SAVE TRANSACTION tr3
INSERT INTO tab1
VALUES (1);
SAVE TRANSACTION tr4;
INSERT INTO tab1
VALUES (2);
BEGIN try
-- change the order of the follwoing two lines around to see the difference
INSERT INTO tab1 VALUES (1 / 0); --Results in a rollback to savepoint
INSERT INTO tab1 VALUES ('OK'); --Results in a complete rollback
COMMIT TRANSACTION tr4;
END try
BEGIN catch
IF Xact_state() = -1
BEGIN
PRINT 'rollback transaction no other work can be done'
ROLLBACK TRANSACTION;
END
ELSE
BEGIN
PRINT 'rollback to savepoint'
ROLLBACK TRANSACTION tr4
END
END catch
IF Xact_state() > 0
BEGIN
INSERT INTO tab1
VALUES (3);
INSERT INTO tab1
VALUES (4);
COMMIT TRANSACTION tr1;
SELECT *
FROM tab1;
DROP TABLE tab1
END