"Begin transaction" usage in multiple delete/insert - sql-server

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.

Related

SQL Server Partial commit in transaction

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.

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.

How to handle nested transaction in stored procedure

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.

Sql Transaction doesn't do complete rollback

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?

Recommendations regarding nested transactions in SQL Server

I have some "base operation" stored procedures, like BookAVehicle and UnBookAVehicle. They are both in a transaction.
But now I need to have a somewhat more complex stored procedure: RescheduleBooking. It also needs to be transactional.
Now, from within ResceduleBooking I want to call BookAVehicle, and in this case I don't want the inner transaction to rollback.
But when I call BookAVehicle directly, I want to keep the rollback.
Any suggestion on how to do this elegantly?
I was thinking of something along the lines of having a "wrapper" stored procedure that as a parameter takes the name of a stored procedure and only contains a transaction and a call to the parameter stored procedure.
So when I call it "directly" I call:
TransactionWrapper(BookAVehicleWithoutTrans)
and when I call it from another transaction I call:
RescheduleBooking -> BookAVehicleWithoutTrans
When you do a BEGIN TRANSACTION an internal counter is incremented ##TRANCOUNT. ROLLBACK TRANSACTION will rollback all BEGIN TRANSACTIONS setting ##TRANCOUNT to 0. Doing a commit transaction will only decrement ##TRANCOUNT, it will do a full commit when ##TRANCOUNT is 1 before setting it to 0.
With that in mind, Assuming you have paired BEGIN and COMMIT TRANSACTIONS in your Book and UnBook procedures I would do the RescheduleBooking procedure something like the following which will maintain the first book even if the unbook fails...
CREATE PROCEDURE RescheduleBooking ...
AS
BEGIN
BEGIN TRY
BEGIN TRANSACTION
EXEC BookAVehicle ...
COMMIT TRANSACTION
END TRY
BEGIN CATCH
IF ##TRANCOUNT > 0
BEGIN
ROLLBACK TRANSACTION
END
RETURN
END CATCH;
-- If the unbook fails the booking above will still stay.
BEGIN TRY
BEGIN TRANSACTION
EXEC UnBookAVehicle ...
COMMIT TRANSACTION
END TRY
BEGIN CATCH
IF ##TRANCOUNT > 0
BEGIN
ROLLBACK TRANSACTION
END
RETURN
END CATCH;
END

Resources