Record Transaction status commit/rollback - sql

I have multiple delete statements which will look similar to what I have written below. However,I want to record the row_count for each table deletes and also the status of transaction.
How do I get if the delete in the transaction completed successfully or not and record that in the Delete_Log_table that I have. Also, what if the delete fail, I will have to stop the deletes from going forward.
Please help :
BEGIN TRANSACTION
DELETE FROM [DB].[SCHEMA].[EXAMPLE_TABLE]
WHERE MY_ID IN (SELECT MY_ID FROM #TEMP_TABLE)
COMMIT TRANSACTION
INSERT INTO [DELETE_LOG_TABLE]
(TABLE_NAME, ROW_COUNT_DELETED, STATUS_OF_TRANSACTION, LOAD_DATE)
('EXAMPLE_TABLE',##ROWCOUNT, 'HOW DO I GET COMMIT/ROLLBACK HERE- HELP', GETDATE())

you need to use TRY and CATCH for what you need. Here is your example:
BEGIN TRY
BEGIN TRANSACTION
DELETE FROM [DB].[SCHEMA].[EXAMPLE_TABLE]
WHERE MY_ID IN (SELECT MY_ID FROM #TEMP_TABLE)
INSERT INTO [DELETE_LOG_TABLE]
(TABLE_NAME, ROW_COUNT_DELETED, STATUS_OF_TRANSACTION, LOAD_DATE)
('EXAMPLE_TABLE',##ROWCOUNT, 'Successful', GETDATE())
COMMIT TRANSACTION
END TRY
BEGIN CATCH
INSERT INTO [DELETE_LOG_TABLE]
(TABLE_NAME, ROW_COUNT_DELETED, STATUS_OF_TRANSACTION, LOAD_DATE)
('EXAMPLE_TABLE',0, 'Error. Detail is :'+ERROR_MESSAGE(), GETDATE())
ROLLBACK TRANSACTION
END CATCH

Related

SQL Server does not rollback trigger transaction to savepoint

I am facing an issue trying to set up a transaction within a trigger of my view. Here is my DDL setup:
CREATE TABLE entity1 (
id INT NOT NULL IDENTITY PRIMARY KEY,
attr1 INT NOT NULL,
attr2 INT NOT NULL
);
GO
CREATE TABLE entity2 (
entity1_id INT NOT NULL FOREIGN KEY REFERENCES entity1(id),
attr3 INT NOT NULL,
attr4 INT NOT NULL
);
GO
CREATE VIEW my_view AS
SELECT attr1, attr2, attr3, attr4
FROM entity1 AS e1
INNER JOIN entity2 AS e2
ON e1.id = e2.entity1_id;
GO
CREATE TRIGGER tg_my_view_ins ON my_view
INSTEAD OF INSERT AS
BEGIN
BEGIN TRY
SAVE TRANSACTION here; -- checkpoint
INSERT INTO entity1 (attr1, attr2)
SELECT attr1, attr2 FROM inserted;
INSERT INTO entity2 (entity1_id, attr3, attr4)
SELECT SCOPE_IDENTITY(), attr3, attr4 FROM inserted;
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION here; -- rollback to checkpoint in case on an error
END CATCH
END
GO
As you can see, I make a savepoint while in the trigger, and rollback in case of any errors (I assume that constraint errors are also handled by TRY/CATCH blocks).
The problem is, when I execute bad inserts within the transaction, trigger error handling block does not rollback:
BEGIN TRY
BEGIN TRANSACTION;
-- successful insert
INSERT INTO my_view (attr1, attr2, attr3, attr4) VALUES (1,2,3,4);
SELECT * FROM entity1; -- one entity
-- i wrap the bad insert into try/catch so the error is discarded,
-- but still rolled back
BEGIN TRY
INSERT INTO my_view (attr1, attr2, attr3) VALUES (3,2,1);
END TRY
BEGIN CATCH
END CATCH;
SELECT * FROM entity1; -- should only have one entity, but has two
ROLLBACK; -- discard the whole transaction
END TRY
BEGIN CATCH
ROLLBACK; -- discard the whole transaction in case of any errors
END CATCH;
I do not seem to be able to set up the trigger the way it would not create orphan records in case of an error. I have tried using BEGIN TRANSACTION here and COMMIT TRANSACTION here within my trigger instead of SAVE TRANSACTION here as well, with no luck. What is the correct way to handle constraint erros within triggers?
The execution setup I would like to keep the way it is, if possible. I create and rollback the transaction for testing purposes. I wrap the bad insert into a try/catch block to discard the error I know should happen.
This seemingly confused behaviour can be made clear by adding error logging into your catch blocks. The following modification of your test code adds error logging (and some other improvements), which shows what actually happens in the process:
begin try
begin transaction;
INSERT INTO dbo.my_view (attr1, attr2, attr3, attr4) VALUES (1,2,3,4);
SELECT * FROM dbo.entity1;
BEGIN TRY
INSERT INTO dbo.my_view (attr1, attr2, attr3) VALUES (3,2,1);
END TRY
BEGIN CATCH
-- Logging - inner CATCH
select 'Inner', ##trancount, error_number(), error_message(), error_procedure(), error_line();
END CATCH;
select * from dbo.entity1;
rollback;
end try
begin catch
-- Logging - outer CATCH
select 'Outer', ##trancount, error_number(), error_message(), error_procedure(), error_line();
-- Conditional rollback, because some errors always terminate the transaction
if ##trancount > 0
rollback;
end catch;
If you run this code with your trigger intact, you will see an error caught by the inner CATCH:
3931
The current transaction cannot be committed and cannot be rolled
back to a savepoint. Roll back the entire transaction.
Searching by the error number leads to this post with a similar question. In his answer, Rutzky shows that the culprit of this behaviour is the XACT_ABORT session option which is apparently set to ON for triggers by default. If your intent is to pursue your trigger-based architecture, then turning this option off inside your trigger will help:
create or alter trigger dbo.tg_my_view_ins
on dbo.my_view
instead of insert as
-- Implicitly set to ON in triggers by default; makes error handling impossible
set xact_abort off;
begin try
save transaction here;
INSERT INTO dbo.entity1 (attr1, attr2)
SELECT attr1, attr2 FROM inserted;
INSERT INTO dbo.entity2 (entity1_id, attr3, attr4)
SELECT e.id, attr3, attr4
FROM inserted i
-- The actual JOIN condidions should reference a natural key in the master table.
-- This is just an example.
inner join dbo.entity1 e on e.attr1 = i.attr1 and e.attr2 = i.attr2;
end try
begin catch
if ##trancount > 0
rollback transaction here;
end catch;
return;
GO
(Again, I have corrected several other issues with your code.)

Transaction doesn't rollback even there was an error?

I created this transaction (my first transaction), there is no row with Id = 111 in Teacher table, however it works on the Subject table and inserted a new row. Shouldn't it rollback all the changes?
BEGIN TRANSACTION
INSERT INTO Subject (Name, SupervisorId) VALUES('Statistics', 4)
UPDATE Teacher SET Name ='Hady' WHERE Id=111
COMMIT TRANSACTION
I then add some more logic to it, but still not working:
Begin Try
BEGIN TRANSACTION
INSERT INTO Subject (Name, SupervisorId) VALUES('Statistics', 4)
UPDATE TeacherO SET Name ='Hady' WHERE Id=111
COMMIT TRANSACTION
End Try
Begin Catch
ROLLBACK TRANSACTION
End Catch
If I understand this correctly, you assume, that the attempt to update a row in teacher table when there is no row with Id = 111 in Teacher table was an error...
An error occurs, when you do something forbidden or impossible. If you try to add a number to a string without casts. Or if you try to get hands on a not exisiting object. Maybe you want to convert 30.02.2016 to date. All will be an error.
But if you tell the database to update all rows where the id=111, exactly this happens: All rows with id=111 will be updated. The count of affected rows will be zero in your case. But this is not an error...
You have to use XACT_ABORT set option to ensure it rolls back. Without it some errors will not cause a roll back.
SET XACT_ABORT ON;
BEGIN TRY
BEGIN TRANSACTION;
INSERT INTO Subject (Name, SupervisorId) VALUES('Statistics', 4);
UPDATE TeacherO SET Name ='Hady' WHERE Id=111;
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION;
END CATCH

Record Counts in Stored Procedures

I have a SP that INSERT INTO TBL_DOMAIN from TBL_STAGING, but first I want check table TBL_STAGING to make sure the table is not empty before I truncate table TBL_DOMAIN, if table TBL_STAGING got more than one record then proceed the truncate table TBL_DOMAIN then run the INSERT, ELSE message say the table TBL_STAGING is EMPTY and exit the SP. My goal is to make sure the table TBL_DOMAIN still have the data even is old. I'm very new SQL please help.
CREATE PROCEDURE [dbo].[SP_INSERT_ALL_DOMAIN]
WITH EXECUTE AS CALLER
AS
BEGIN
BEGIN TRANSACTION NT_ALL_DOMAIN
INSERT INTO TBL_DOMAIN
(DOMAIN_NAME,
DISTINGUISHED_NAME,
EMAIL_ADDR_I)
SELECT DOMAIN_NAME,
DISTINGUISHED_NAME,
EMAIL_ADDR_I
FROM TBL_STAGING
First you need to check if TBL_STAGING has data:
IF EXISTS (SELECT TOP 1 1 FROM TBL_STAGING)
BEGIN
BEGIN TRANSACTION NT_ALL_DOMAIN
INSERT INTO TBL_DOMAIN
(DOMAIN_NAME,
DISTINGUISHED_NAME,
EMAIL_ADDR_I)
SELECT DOMAIN_NAME,
DISTINGUISHED_NAME,
EMAIL_ADDR_I
FROM TBL_STAGING
COMMIT
END
ELSE
BEGIN
RETURN 'no data on table'
END

Transaction roll back not working in Postgresql

I am working on PostgreSQL 9.1.4 .
I am inserting the data into 2 tables its working nicely.
I wish to apply transaction for my tables both table exist in
same DB. If my 2nd table going fail on any moment that time my 1 st
table should be rollback.
I tried the properties in "max_prepared_transactions" to a non zero
value in /etc/postgres/postgres.conf. But Still Transaction roll
back is not working.
in postgresql you cannot write commit or roll back explicitly within a function.
I think you could have use a begin end block
just write it simple
BEGIN;
insert into tst_table values ('ABC');
Begin
insert into 2nd_table values ('ABC');
EXCEPTION
when your_exception then
ROLL BACK;
END;
END;
Probably you didn't started transaction.
Please, try
BEGIN;
INSERT INTO first_table VALUES(10);
-- second insert should fail
INSERT INTO second_table VALUES(10/0);
ROLLBACK;
I think it would be helpfull
create proc DataInsertInTable
as
begin tran
insert into Table1 values('Table1Data','XYZ')
if(##ERROR <>0)
begin
rollback tran;
return 0
end
insert into Table2 values('Table2Data','ABC')
if(##ERROR <>0)
begin
rollback tran;
return 0
end
commit Tran
return 1

Will ROLLBACK TRAN roll everything back?

Will ROLLBACK TRAN rollback everything in my SP? (including the call to another SP)
Example:
BEGIN TRAN
INSERT INTO (table1)
VALUES (1,'abc')
EXEC InsertTable2
INSERT INTO (table3)
VALUES (1,'abc')
ROLLBACK TRAN
from msdn
Rolls back an explicit or implicit transaction to the beginning of the transaction, or to a savepoint inside the transaction. You can use ROLLBACK TRANSACTION to erase all data modifications made from the start of the transaction or to a savepoint. It also frees resources held by the transaction.
so YES
Without knowing the definition of InsertTable2 impossible to say for sure. Here's an example where it doesn't.
CREATE TABLE table1
(
Num int,
String char(3)
)
GO
CREATE PROC InsertTable2
AS
COMMIT;
BEGIN TRAN;
GO
BEGIN TRAN
INSERT INTO table1
VALUES (1,'abc')
EXEC InsertTable2
INSERT INTO table1
VALUES (1,'abc')
ROLLBACK TRAN
GO
SELECT *
FROM table1
GO
DROP TABLE table1
DROP PROC InsertTable2
But assuming a sane definition the answer is "yes"
Yes. It will ROLLBACK everything in the Store Procedure that you have mentioned above.
More on ROLLBACK TRANSACTION