SQL Transactions with Commit and Rollback - sql

Can someone tell me if I am correct in understanding the use of START TRANSACTION and COMMIT or ROLLBACK.
If I want to update a table but want the ability to UNDO my update in case of an error would this code be the proper way to go about it?
START TRANSACTION;
update ar
set doc_no = gltrans.reference
from plxx.dbo.ar
inner join plxx.dbo.gltrans on gltrans.LNKUNIQUE = ar.UNIQUE_CD
where gltrans.LNKUNIQUE = ar.UNIQUE_CD
and ar.DOC_NO <> gltrans.REFERENCE
COMMIT;

Here is a pattern that I use for rolling back a transaction on error in a sql statement:
https://web.archive.org/web/20211020150034/http://www.4guysfromrolla.com/webtech/041906-1.shtml
BEGIN TRY
BEGIN TRANSACTION -- Start the transaction
update ar
set doc_no = gltrans.reference
from plxx.dbo.ar
inner join plxx.dbo.gltrans on gltrans.LNKUNIQUE = ar.UNIQUE_CD
where gltrans.LNKUNIQUE = ar.UNIQUE_CD
and ar.DOC_NO <> gltrans.REFERENCE
-- 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

Yes, if you have an error occur after starting a transaction & before the commit statement anything within that statement will be rolled back. You can also include Try/Catch statements if you want some additional functionality around error management, for example logging of the failure.

Use BEGIN TRANSACTION instead of using START TRANSACTION. after executing the UPDATE command, you can roll it back by using ROLLBACK then to confirm the transaction use COMMIT.

Related

Error with uncommitted transaction

I have the following query that I ran in SQL Server Management Studio:
BEGIN TRANSACTION [Tran1]
BEGIN TRY
UPDATE SomeTable
SET value = 0
WHERE username = 'test'
OR [PR_RequestDate] < DATEADD(day, -2, GETDATE())
INSERT INTO SomeTable (username, value)
VALUES('test', 'test')
COMMIT TRANSACTION [Tran1]
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION [Tran1]
END CATCH
GO
After executing it every other query that I try to run on the same table runs forever, and when I try to exist SQL Server Management Studio, I get a warning that there are uncommitted transactions.
What exactly is wrong with my query and how should I fix it?
Typos will be caught immediately,further as per Ivan suggestionmyou are not showing us entire info.You are getting uncommitted transactions error due to error in update or insert and the entire query trying to rollback..You can do the follow to get total message and trancount,,
set xact_abort on--to rollback entire batch,rollback in catch is redundant
begin try
begin tran
commit
end try
begin catch
select ##trancount--gives me number of open transactions
select error_message()
rollback tran
end catch

Batchwise Script Execution

I have long script which contains the Create tables, create schemas,insert data,update tables etc.I have to do this by only on script in batch wise.I ran it before but it created every time some error due to this some object will present inside the database. So In need some mechanism which can handle the batch execution if something goes wrong the whole script should be rolled back.
Appreciated Help and Time.
--343
Try this:
DECLARE #outer_tran int;
SELECT #outer_tran = ##TRANCOUNT;
-- find out whether we are inside the outer transaction
-- if yes - creating save point if no starting own transaction
IF #outer_tran > 0 SAVE TRAN save_point ELSE BEGIN TRAN;
BEGIN TRY
-- YOUR CODE HERE
-- if no errors and we have started own transaction - commit it
IF #outer_tran = 0 COMMIT;
END TRY
BEGIN CATCH
-- if error occurred - rollback whole transaction if it is own
-- or rollback to save point if we are inside the external transaction
IF #outer_tran > 0 ROLLBACK TRAN save_point ELSE ROLLBACK;
--and rethrow original exception to see what happens
DECLARE
#ErrorMessage nvarchar(max),
#ErrorSeverity int,
#ErrorState int;
SELECT
#ErrorMessage = ERROR_MESSAGE() + ' Line ' + cast(ERROR_LINE() as nvarchar(5)),
#ErrorSeverity = ERROR_SEVERITY(),
#ErrorState = ERROR_STATE();
RAISERROR (#ErrorMessage, #ErrorSeverity, #ErrorState);
END CATCH
While I might not have caught all the nuances of your question, I believe XACT_ABORT will deliver the functionality you seek. Simply add a
SET XACT_ABORT ON;
to the beginning of your script.
With the 2005 release of SQL Server, you have access to try/catch blocks in TSQL as well.

Transaction handling in Trigger (TRY/CATCH....XACT_ABORT ON)

I have the process scenario on SQL Server 2008R2:
• A usp to gather data and then a transfer data between two SQL Servers
This process is to be done with transaction at all levels of the process (usp, SSIS, and trigger)
In the data flow transferring the data to DB7.dbo.Dest, this table has an AFTER INSERT trigger which inserts the data that just came through into the final table DB7.dbo.FinalDestination:
CREATE TRIGGER [dbo].[Insert_OnStaging] ON [dbo].[Dest]
AFTER INSERT, UPDATE
AS
BEGIN
SET NOCOUNT ON;
SET XACT_ABORT ON; --Rollsback complete transaction if there are any errors
BEGIN TRY
BEGIN TRANSACTION
INSERT INTO [DB7].[dbo].[FinalDestination] WITH (TABLOCK)
(Column1
,Column2
)
SELECT I.Column1, I.Column2
FROM INSERTED I
INNER JOIN [DB7].[dbo].[Dest] PR
ON I.IDcol = PR.IDcol
COMMIT TRANSACTION
END TRY
BEGIN CATCH
IF ##TRANCOUNT > 0 AND XACT_STATE() <> 0
ROLLBACK TRANSACTION;
DECLARE #ErrorMessage NVARCHAR(4000);
DECLARE #ErrorSeverity INT;
DECLARE #ErrorState INT;
DECLARE #ErrorLine INT;
SELECT
#ErrorMessage = ERROR_MESSAGE(),
#ErrorSeverity = ERROR_SEVERITY(),
#ErrorState = ERROR_STATE(),
#ErrorLine = ERROR_LINE()
;
RAISERROR (#ErrorMessage, -- Message text.
#ErrorSeverity, -- Severity.
#ErrorState, -- State.
#ErrorLine --Error Line
);
END CATCH;
END
At every level, I have tried to be defensive about the data due to the sensitivity of the data properly and completely reaching the final table.
In regards to the SSIS, from what I've read and tested it seems to work fine.
My biggest concern is the trigger which I scripted above. From my reading and understanding, setting XACT_ABORT ON will roll back the transaction inside the TRY block if any errors (in other words, there is an uncommitable transaction). In this case I went ahead and still added the rollback transaction portion in the CATCH block as a piece of mind since it would never reach (from my understanding). At the same time, I added the WITH (TABLOCK) option in order to lock the table while performing the INSERT.
In the case of the trigger, is the TRY...CATCH even necessary with the XACT_ABORT being ON? Is the COMMIT TRANSACTION necessary inside the TRY block? As I have also seen it committed after the CATCH block based on the ##TRANCOUNT
BEGIN TRY
BEGIN TRANSACTION
[Tsql here]
END TRY
BEGIN CATCH
[Error Handling]
END CATCH
IF ##TRANCOUNT > 0
COMMIT TRANSACTION
END
Answers and critique are welcomed and thank you in advance. Please excuse any typos as I tried to generalize the names...
You need TRY..CATCH even though you're using XACT_ABORT. XACT_ABORT aborts the tran but continues running the batch/procedure! This is very, very nasty behavior. It means that DML/DDL can still run after an error has occurred but outside of a transaction so that you can never roll it back.
SQL Server does not have any mechanism to avoid that except for TRY..CATCH. I'm not sure what XACT_ABORT is ever good for. In your example neither does it help not does it hurt.
And yes, you can move the COMIT outside the TRY if you want to. Just make sure to properly balance it with the BEGIN TRAN.

TSQL error checking code not working

I'm trying to add an alert to my log for a running insert of a view into a table when there is an error executing the query in the view. When I run the view alone, I get an invalid input into SUBSTRING (the exact wording of the error I can't remember). When I run it as part of my view -> table stored procedure, the error is ignored, then I have to go digging for the offending line and make an exception in the view's code to omit that line from the results (I know, it sounds kludge-y, but I'm doing data reduction on huge web-log files from a specialized webapp), but I digress.
I've tried two different methods for trying to catch the error and neither are triggered in such a way to insert the row indicating an error in my execution result table (refresh_results). I think I may be missing some fundamental - perhaps the errors are being encapsulated in come way. If I can't detect the error, the only way to notice an error is if someone notices the number of entries into the table is low for a given period of time.
SELECT #TransactionName = 'tname';
BEGIN TRANSACTION #TransactionName;
BEGIN TRY
print 'tname ***In Try***';
if exists (select name from sysobjects where name='tablename')
begin
drop table tablename;
end
select * into tablename
from opendatasource('SQLNCLI', 'Data Source=DATABASE;UID=####;password=####').dbo.viewname;
COMMIT TRANSACTION #TransactionName;
END TRY
BEGIN CATCH
print 'tablename ***ERROR - check for SUBSTRING***';
begin transaction
set #result_table = 'tablename ***ERROR - check for SUBSTRING***'
select #result_time = getdate(),
#result_rows = count(logtime)
from tablename
insert INTO [dbo].[refresh_results] (result_time, result_table, result_rows)
values (#result_time, #result_table, #result_rows);
commit transaction
ROLLBACK TRANSACTION #TransactionName;
END CATCH
or
if exists (select name from sysobjects where name='tablename')
begin
drop table tablename;
end
select * into tablename
from opendatasource('SQLNCLI', 'Data Source=DATABASE;UID=####;password=####').dbo.viewname;
print '##error'
print ##error
if ##error <> 0
Begin
print 'tablename ***ERROR - check for SUBSTRING***';
set #result_table = 'tablename ***ERROR - check for SUBSTRING***'
select #result_time = getdate(),
#result_rows = count(logtime)
from tablename
insert INTO [dbo].[refresh_results] (result_time, result_table, result_rows)
values (#result_time, #result_table, #result_rows);
End
Your nested transactions aren't doing what you think. You are rolling back the error you thought you stored. Roll back the initial transaction and then, if you feel the need, start a new transaction for logging the error.
See here.
You have two seperate problems
In your first example you are running transactions that do the following:
BEGIN TRAN
...error...
BEGIN TRAN
...log error...
COMMIT TRAN
ROLLBACK TRAN
The inner transaction is rolled back with the outer transaction. Maybe try:
BEGIN TRAN
...error...
ROLLBACK TRAN
BEGIN TRAN
...log error...
ROLLBACK TRAN
The second example you are using ##ERROR. As I understand it as soon as you run something ##ERROR is replaced. That something I think includes the print statement.
If you change it to something like:
DECLARE #Error INT
select * into tablename
from opendatasource('SQLNCLI', 'Data Source=DATA3;UID=;password=').dbo.viewname;
SET #Error = ##ERROR
print '##error'
print #Error
if #Error <> 0
...log the error
The advantage of the TRY CATCH is that if you have an error it will catch it. The ##ERROR method works 100% but it only works on the last line run. so if you have an error with DROP TABLE tablename ##ERROR won't get it (unless you add another check)
Ok, so I had to use a helper procedure to add a log entry. I think what was going on is that the rollback was also rolling back the log entry.
This is what I had to do:
DECLARE #myError tinyint;
BEGIN TRY
BEGIN TRANSACTION;
if exists (select name from sys.sysobjects where name='table_name')
begin
drop table table_name
end
select * into table_name
from opendatasource('SQLNCLI', 'Data Source=###;UID=###;password=###').view_Table
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
set #myError = 1
ROLLBACK TRANSACTION;
END CATCH
if #myError <> 0
begin
exec dbo.table error
end
ELSE
EXEC exec dbo.table normal row

SQL Server - transactions roll back on error?

We have client app that is running some SQL on a SQL Server 2005 such as the following:
BEGIN TRAN;
INSERT INTO myTable (myColumns ...) VALUES (myValues ...);
INSERT INTO myTable (myColumns ...) VALUES (myValues ...);
INSERT INTO myTable (myColumns ...) VALUES (myValues ...);
COMMIT TRAN;
It is sent by one long string command.
If one of the inserts fail, or any part of the command fails, does SQL Server roll back the transaction? If it does not rollback, do I have to send a second command to roll it back?
I can give specifics about the api and language I'm using, but I would think SQL Server should respond the same for any language.
You can put set xact_abort on before your transaction to make sure sql rolls back automatically in case of error.
You are correct in that the entire transaction will be rolled back. You should issue the command to roll it back.
You can wrap this in a TRY CATCH block as follows
BEGIN TRY
BEGIN TRANSACTION
INSERT INTO myTable (myColumns ...) VALUES (myValues ...);
INSERT INTO myTable (myColumns ...) VALUES (myValues ...);
INSERT INTO myTable (myColumns ...) VALUES (myValues ...);
COMMIT TRAN -- Transaction Success!
END TRY
BEGIN CATCH
IF ##TRANCOUNT > 0
ROLLBACK TRAN --RollBack in case of Error
-- <EDIT>: From SQL2008 on, you must raise error messages as follows:
DECLARE #ErrorMessage NVARCHAR(4000);
DECLARE #ErrorSeverity INT;
DECLARE #ErrorState INT;
SELECT
#ErrorMessage = ERROR_MESSAGE(),
#ErrorSeverity = ERROR_SEVERITY(),
#ErrorState = ERROR_STATE();
RAISERROR (#ErrorMessage, #ErrorSeverity, #ErrorState);
-- </EDIT>
END CATCH
Here the code with getting the error message working with MSSQL Server 2016:
BEGIN TRY
BEGIN TRANSACTION
-- Do your stuff that might fail here
COMMIT
END TRY
BEGIN CATCH
IF ##TRANCOUNT > 0
ROLLBACK TRAN
DECLARE #ErrorMessage NVARCHAR(4000) = ERROR_MESSAGE()
DECLARE #ErrorSeverity INT = ERROR_SEVERITY()
DECLARE #ErrorState INT = ERROR_STATE()
-- Use RAISERROR inside the CATCH block to return error
-- information about the original error that caused
-- execution to jump to the CATCH block.
RAISERROR (#ErrorMessage, #ErrorSeverity, #ErrorState);
END CATCH
From MDSN article, Controlling Transactions (Database Engine).
If a run-time statement error (such as a constraint violation) occurs in a batch, the default behavior in the Database Engine is to roll back only the statement that generated the error. You can change this behavior using the SET XACT_ABORT statement. After SET XACT_ABORT ON is executed, any run-time statement error causes an automatic rollback of the current transaction. Compile errors, such as syntax errors, are not affected by SET XACT_ABORT. For more information, see SET XACT_ABORT (Transact-SQL).
In your case it will rollback the complete transaction when any of inserts fail.
If one of the inserts fail, or any part of the command fails, does SQL server roll back the transaction?
No, it does not.
If it does not rollback, do I have to send a second command to roll it back?
Sure, you should issue ROLLBACK instead of COMMIT.
If you want to decide whether to commit or rollback the transaction, you should remove the COMMIT sentence out of the statement, check the results of the inserts and then issue either COMMIT or ROLLBACK depending on the results of the check.