I was creating a stored procedure and I see some differences between my methodology and my colleague's.
I am using SQL Server 2005
My Stored procedure looks like this
BEGIN TRAN
BEGIN TRY
INSERT INTO Tags.tblTopic
(Topic, TopicCode, Description)
VALUES(#Topic, #TopicCode, #Description)
INSERT INTO Tags.tblSubjectTopic
(SubjectId, TopicId)
VALUES(#SubjectId, ##IDENTITY)
COMMIT TRAN
END TRY
BEGIN CATCH
DECLARE #Error VARCHAR(1000)
SET #Error= 'ERROR NO : '+ERROR_NUMBER() + ', LINE NO : '+ ERROR_LINE() + ', ERROR MESSAGE : '+ERROR_MESSAGE()
PRINT #Error
ROLLBACK TRAN
END CATCH
And my colleague's way of writing looks like the below one
BEGIN TRY
BEGIN TRAN
INSERT INTO Tags.tblTopic
(Topic, TopicCode, Description)
VALUES(#Topic, #TopicCode, #Description)
INSERT INTO Tags.tblSubjectTopic
(SubjectId, TopicId)
VALUES(#SubjectId, ##IDENTITY)
COMMIT TRAN
END TRY
BEGIN CATCH
DECLARE #Error VARCHAR(1000)
SET #Error= 'ERROR NO : '+ERROR_NUMBER() + ', LINE NO : '+ ERROR_LINE() + ', ERROR MESSAGE : '+ERROR_MESSAGE()
PRINT #Error
ROLLBACK TRAN
END CATCH
Here the only difference between the two methods is the position of Begin TRAN.
According to me my colleague's method should not work when an exception occurs i.e. Rollback should not get executed because TRAN doesn't have scope in method 2. But when I tried to run both the methods, they were working in the same way.
In Method 1, scope of TRAN is outside of try block so it should be visible in both try block and catch block and should give result as per the scope methodology of programming works.
In Method 2, scope of TRAN is limited within Try block so Commit and Rollback should occur within the try block and should throw exception when a Rollback with no Begin Tran exists in catch block, but this is also working perfectly.
I am confused about how TRANSACTION works. Is it scope-free?
Transactions are not "scoped" in the way that programming languages are.
Transactions are nested for the current connection. Each BEGIN TRAN starts a new transaction and this transaction ends whenever a COMMIT or ROLLBACK is called, it does not matter where in your stored proc this is.
Transactions are nested for the
current connection. Each BEGIN TRAN
starts a new transaction and this
transaction ends whenever a COMMIT or
ROLLBACK is called, it does not matter
where in your stored proc this is.
only to add that ROLLBACK ends "all" open transactions for the connection...
Related
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.
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
i have a question regarding using Transaction. Consider this code:
declare #trans_name varchar(max) = 'Append'
begin tran #trans_name
insert into SIDB_Module
(module_name, module_description, modulelevel, parentid, issystem, iscurrent)
values
(#module_name, #module_description, #modulelevel, #parentid, #issystem, 1)
set #moduleid = SCOPE_IDENTITY()
declare #id int = OBJECT_ID('SIDB_Module')
exec usp_M_SIDB_TransactionInformation_App_Append
#moduleid, id, 'append' ,#createdby_userid
if ##ERROR <> 0
rollback tran #trans_name
commit tran #trans_name
does transaction still apply on this.. even the next insert query is on the other stored procedure??
Yes, the call to usp_M_SIDB_TransactionInformation_App_Append is part of the transaction
Note: your error handling is "old style" (using ##ERROR) from SQL Server 2000 and will generate errors (error 266) if the inner proc rolls back or commits.
For more, see Nested stored procedures containing TRY CATCH ROLLBACK pattern?
I'm interested in the side effects and potential problems of the following pattern:
CREATE PROCEDURE [Name]
AS
BEGIN
BEGIN TRANSACTION
BEGIN TRY
[...Perform work, call nested procedures...]
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION
RAISERROR [rethrow caught error using #ErrorNumber, #ErrorMessage, etc]
END CATCH
END
To the best of my understanding this pattern is sound when used with a single procedure - the procedure will either complete all of its statements without error, or it will rollback all actions and report the error.
However when one stored procedure calls another stored procedure to do some sub-unit of work (with the understanding that the smaller procedure is sometimes called on its own) I see an issue coming about with relation to rollbacks - an informational message (Level 16) is issued stating The ROLLBACK TRANSACTION request has no corresponding BEGIN TRANSACTION.. This I assume is because the rollback in the sub-procedure is always rolling back the outer-most transaction, not just the transaction started in the sub-procedure.
I do want the whole thing rolled back and aborted if any error occurs (and the error reported to the client as an SQL error), I'm just not sure of all the side effects that come from the outer layers trying to rollback a transaction that has already been rolled back. Perhaps a check of ##TRANCOUNT before doing a rollback at each TRY CATCH layer?
Finally there is the client end (Linq2SQL), which has it's own transaction layer:
try
{
var context = new MyDataContext();
using (var transaction = new TransactionScope())
{
// Some Linq stuff
context.SubmitChanges();
context.MyStoredProcedure();
transactionComplete();
}
}
catch
{
// An error occured!
}
In the event that a stored procedure, "MySubProcedure", called inside MyStoredProcedure raises an error, can I be sure that everything previously done in MyStoredProcedure will be rolled back, all the Linq operations made by SubmitChanges will be rolled back, and finally that the error will be logged? Or what do I need to change in my pattern to ensure the whole operation is atomic, while still allowing the child parts to be used individually (i.e. the sub-procedures should still have the same atomic protection)
This is our template (error logging removed)
This is designed to handle
Paul Randal's article "No such thing as a nested transaction in SQL Server"
Error 266
Trigger Rollbacks
Explanations:
all TXN begin and commit/rollbacks must be paired so that ##TRANCOUNT is the same on entry and exit
mismatches of ##TRANCOUNT cause error 266 because
BEGIN TRAN increments ##TRANCOUNT
COMMIT decrements ##TRANCOUNT
ROLLBACK returns ##TRANCOUNT to zero
You can not decrement ##TRANCOUNT for the current scope
This is what you'd think is the "inner transaction"
SET XACT_ABORT ON suppresses error 266 caused by mismatched ##TRANCOUNT
And also deals with issues like this "SQL Server Transaction Timeout" on dba.se
This allows for client side TXNs (like LINQ)
A single stored procedure may be part of distributed or XA transaction, or simply one initiated in client code (say .net TransactionScope)
Usage:
Each stored proc must conform to the same template
Summary
So don't create more TXNs than you need
The code
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;
THROW;
--before SQL Server 2012 use
--RAISERROR [rethrow caught error using #ErrorNumber, #ErrorMessage, etc]
END CATCH
GO
Notes:
The rollback check is actually redundant because of SET XACT_ABORT ON. However, it makes me feel better, looks odd without, and allows for situations where you don't want it on
Remus Rusanu has a similar shell that uses save points. I prefer an atomic DB call and don't use partial updates like their article
I am not a Linq guy (and neither is Erland), but he wrote the absolute bibles on error handling. Outside of the complications Linq might add to your problem, all of your other questions should be answered here:
http://www.sommarskog.se/error_handling/Part1.html
(Old link: http://www.sommarskog.se/error_handling_2005.html)
To solve the issue of returning the error number and line number mentioned by #AlexKuznetsov, one can raise the error as such:
DECLARE #ErrorMessage NVARCHAR(4000)
DECLARE #ErrorSeverity INT
DECLARE #ErrorState INT
DECLARE #ErrorLine INT
DECLARE #ErrorNumber INT
SELECT #ErrorMessage = ERROR_MESSAGE(),
#ErrorSeverity = ERROR_SEVERITY(),
#ErrorState = ERROR_STATE(),
#ErrorNumber = ERROR_NUMBER(),
#ErrorLine = ERROR_LINE()
RAISERROR (#ErrorMessage, #ErrorSeverity, #ErrorState, #ErrorNumber, #ErrorLine)
-- #Amanda method above doesnt return correct error number
DECLARE
#ErrorMessage nvarchar(4000),
#ErrorSeverity int,
#ErrorState int,
#ErrorLine int,
#ErrorNumber int
BEGIN TRY
SELECT 1/0; -- CATCH me
END TRY
BEGIN CATCH
DECLARE #err int = ##ERROR
PRINT #err -- 8134, divide by zero
PRINT ERROR_NUMBER() -- 8134
SELECT
#ErrorMessage = ERROR_MESSAGE(),
#ErrorSeverity = ERROR_SEVERITY(),
#ErrorState = ERROR_STATE(),
#ErrorNumber = ERROR_NUMBER(),
#ErrorLine = ERROR_LINE()
-- error number = 50000 :(
RAISERROR (#ErrorMessage, #ErrorSeverity, #ErrorState, #ErrorNumber, #ErrorLine)
END CATCH
-- error number = 8134
SELECT 1/0
In case no special error handling needed in CATCH except rethrow and stored procs call chain isn't too long it may be suitable to use such simple template:
create procedure someNestedSP
as
SET XACT_ABORT ON
begin transaction
-- do some work or call some other similar SP
commit transaction
It would also rollback root transaction with all "nested" ones in case of any error but the code is shorter and more straightforward than #gbn's solution. Still XACT_ABORT takes care of most issues mentioned there.
There may be addiotional overhead for transaction nesting but it may be not too high, I guess.
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.