BEGIN TRANSACTION;
BEGIN TRY
ALTER TABLE dbo.SomeLogs
ADD SomeID NVARCHAR(250) NULL
ALTER TABLE dbo.SomeLogs
ADD SomeID NVARCHAR(250) NULL
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION;
DECLARE #Msg NVARCHAR(MAX);
SELECT #Msg = ERROR_MESSAGE();
RAISERROR('Error Occured: %s', 20, 101, #Msg) WITH LOG;
END CATCH;
Running the above query gives the following error which is correct as I am trying to add same column twice.
Msg 2705, Level 16, State 4, Line 6
Column names in each table must be unique. Column name 'SomeID' in table 'dbo.SomeLogs' is specified more than once.
But the problem is that the SomeLogs table is locked. When I try to do SELECT on SomeLogs I get this error.
Failed to retrieve data for this request. (Microsoft.SqlServer.Management.Sdk.Sfc)
For help, click: http://go.microsoft.com/fwlink?ProdName=Microsoft%20SQL%20Server&LinkId=20476
ADDITIONAL INFORMATION:
Lock request time out period exceeded. (Microsoft SQL Server, Error: 1222)
For help, click: http://go.microsoft.com/fwlink?ProdName=Microsoft%20SQL%20Server&ProdVer=11.00.3000&EvtSrc=MSSQLServer&EvtID=1222&LinkId=20476
Why is the catch block not catching this error? And how to avoid table getting locked?
Try..Catch
block will not catch this error because it's compilation error, and compilation errors cannot be catched within the current scope.
The table remains locked because locks are not released until transaction is committed or rolled back. When xact_abort is set to off(default for SSMS sessions) transaction is not rolled backed when the compilation error occurs, that is by (bad!) design, and to fix this you should use
set xact_abort on;
You can catch this error in outer scope, for example, if you wrap this code in stored procedure or dynamic code, executing sp/dynamic code within try..catch
Using TRY…CATCH with XACT_STATE
https://learn.microsoft.com/en-us/sql/t-sql/language-elements/try-catch-transact-sql
The following example shows how to use the TRY…CATCH construct to
handle errors that occur inside a transaction. The XACT_STATE function
determines whether the transaction should be committed or rolled back.
In this example, SET XACT_ABORT is ON. This makes the transaction
uncommittable when the constraint violation error occurs.
You can specify SET XACT_ABORT ON to automatically rollback the transaction in the event of an error or attention (i.e. client query cancel or timeout). The general TRY/CATCH pattern I suggest in stored procedures and batches in SQL 2012 and later:
SET XACT_ABORT ON;
BEGIN TRY
BEGIN TRAN;
--do stuff
COMMIT;
END TRY
BEGIN CATCH
IF ##TRANCOUNT > 0 ROLLBACK TRANSACTION;
THROW;
END CATCH;
See Erland Sommarskog's error handling article for detailed explanation.
I have an Insert stored procedure which will feed data to Table1 and get the Column1 value from Table1 and call the second stored procedure which will feed the Table2.
But when I call The second stored procedure as:
Exec USPStoredProcName
I get the following error:
Transaction count after EXECUTE indicates a mismatching number of BEGIN and COMMIT statements. Previous count = 1, current count = 0.
I have read the answers in other such questions and am unable to find where exactly the commit count is getting messed up.
If you have a TRY/CATCH block then the likely cause is that you are catching a transaction abort exception and continue. In the CATCH block you must always check the XACT_STATE() and handle appropriate aborted and uncommitable (doomed) transactions. If your caller starts a transaction and the calee hits, say, a deadlock (which aborted the transaction), how is the callee going to communicate to the caller that the transaction was aborted and it should not continue with 'business as usual'? The only feasible way is to re-raise an exception, forcing the caller to handle the situation. If you silently swallow an aborted transaction and the caller continues assuming is still in the original transaction, only mayhem can ensure (and the error you get is the way the engine tries to protect itself).
I recommend you go over Exception handling and nested transactions which shows a pattern that can be used with nested transactions and exceptions:
create procedure [usp_my_procedure_name]
as
begin
set nocount on;
declare #trancount int;
set #trancount = ##trancount;
begin try
if #trancount = 0
begin transaction
else
save transaction usp_my_procedure_name;
-- Do the actual work here
lbexit:
if #trancount = 0
commit;
end try
begin catch
declare #error int, #message varchar(4000), #xstate int;
select #error = ERROR_NUMBER(), #message = ERROR_MESSAGE(), #xstate = XACT_STATE();
if #xstate = -1
rollback;
if #xstate = 1 and #trancount = 0
rollback
if #xstate = 1 and #trancount > 0
rollback transaction usp_my_procedure_name;
raiserror ('usp_my_procedure_name: %d: %s', 16, 1, #error, #message) ;
end catch
end
go
I had this problem too. For me, the reason was that I was doing
return
commit
instead of
commit
return
in one stored procedure.
This normally happens when the transaction is started and either it is not committed or it is not rollback.
In case the error comes in your stored procedure, this can lock the database tables because transaction is not completed due to some runtime errors in the absence of exception handling
You can use Exception handling like below. SET XACT_ABORT
SET XACT_ABORT ON
SET NoCount ON
Begin Try
BEGIN TRANSACTION
//Insert ,update queries
COMMIT
End Try
Begin Catch
ROLLBACK
End Catch
Source
Be aware of that if you use nested transactions, a ROLLBACK operation rolls back all the nested transactions including the outer-most one.
This might, with usage in combination with TRY/CATCH, result in the error you described. See more here.
This can also occur if your stored procedure encounters a compile failure after opening a transaction (e.g. table not found, invalid column name).
I found i had to use 2 stored procedures a "worker" one and a wrapper one with try/catch both with logic similar to that outlined by Remus Rusanu. The worker catch is used to handle the "normal" failures and the wrapper catch to handle compile failure errors.
https://msdn.microsoft.com/en-us/library/ms175976.aspx
Errors Unaffected by a TRY…CATCH Construct
The following types of errors are not handled by a CATCH block when they occur at the same level of execution as the TRY…CATCH construct:
Compile errors, such as syntax errors, that prevent a batch from running.
Errors that occur during statement-level recompilation, such as object name resolution errors that occur after compilation because of deferred name resolution.
Hopefully this helps someone else save a few hours of debugging...
In my case, the error was being caused by a RETURN inside the BEGIN TRANSACTION. So I had something like this:
Begin Transaction
If (#something = 'foo')
Begin
--- do some stuff
Return
End
commit
and it needs to be:
Begin Transaction
If (#something = 'foo')
Begin
--- do some stuff
Rollback Transaction ----- THIS WAS MISSING
Return
End
commit
For me after extensive debugging the fix was a simple missing throw; statement in the catch after the rollback. Without it this ugly error message is what you end up with.
begin catch
if ##trancount > 0 rollback transaction;
throw; --allows capture of useful info when an exception happens within the transaction
end catch
I had the same error message, my mistake was that I had a semicolon at the end of COMMIT TRANSACTION line
Avoid using
RETURN
statement when you are using
BEGIN TRY
...
END TRY
BEGIN CATCH
...
END CATCH
and
BEGIN, COMMIT & ROLLBACK
statements in SQL stored procedures
I encountered this error once after omitting this statement from my transaction.
COMMIT TRANSACTION [MyTransactionName]
In my opinion the accepted answer is in most cases an overkill.
The cause of the error is often mismatch of BEGIN and COMMIT as clearly stated by the error. This means using:
Begin
Begin
-- your query here
End
commit
instead of
Begin Transaction
Begin
-- your query here
End
commit
omitting Transaction after Begin causes this error!
Make sure you don't have multiple transactions in the same procedure/query out of which one or more are left uncommited.
In my case, I accidentally had a BEGIN TRAN statement in the query
This can also depend on the way you are invoking the SP from your C# code. If the SP returns some table type value then invoke the SP with ExecuteStoreQuery, and if the SP doesn't returns any value invoke the SP with ExecuteStoreCommand
For me, the issue was that I forgot to add the output keyword following some output parameters of a SP call within the transaction.
The exact reason for this message is the rule that SQL Server implies: Transaction count should be same at the beginning and the end of execution of a procedure. In other terms, a procedure;
shouldn't commit/rollback a transaction that it didn't start. In this case, previous count displayed in the exception message would be greater zero, and current count is zero. Best way to prevent this is capturing transaction count (##TRANCOUNT) at the very beginning of the execution, and using transaction statements only if it is zero. The sample procedure below is a simplest "safe" structure against this type of mistake. If this procedure is called within an existing transaction, it won't begin a new transaction nor try to commit or rollback the "inherited" one. Instead, it just re-throws the same error to caller context. This is also a good practice to keep the real source procedure of the error.
should decide the fate (commit or rollback) of a transaction it started, before it's execution ends. In this case, current count would be greater than previous count.
I would highly recommend reading Erland Sommarskog's Error and Transaction Handling in SQL Server thoroughly
create or alter proc sp_err266
as
begin
set nocount on
set xact_abort on
declare #trancount int = ##trancount
if #trancount = 0
begin tran
begin try
raiserror('Raise an unexpected error...', 16, 1);
if XACT_STATE() = 1 and #trancount = 0
commit;
end try
begin catch
if XACT_STATE() <> 0 and #trancount = 0
rollback;
else
throw;
end catch
end
If you are having a code structure of something like:
SELECT 151
RETURN -151
Then use:
SELECT 151
ROLLBACK
RETURN -151
For me two begin transactions and multi rollback transaction causing this issue.
------------------------------------------------------------
BEGIN TRANSACTION
-- BEGING TRANSACTION
call of stored procedure -- ROLLBACK TRANASCTION
-- ROLLBACK TRANSACTION
ROLLBACK TRANSACTION
-----------------------------------------------------------
It can rollback only one time, it won't have multi rollback statements, also check the return statements which is causing the issue.
In nested procedures ROLLBACK should be used with care, detailed explanation here https://stackoverflow.com/a/74479802/6204480
I have a DDL trigger to audit any changes to DDL events that occur on the server.
The code in the trigger reads the eventdata and writes it to a table.
I want to wrap that operation in a TSQL try-catch so if it fails for any reason then I would log the fault to the SQL log but let the operation go through, but it doesn't seem to work.
I am already using if exists to make sure the table I need to write to still exists, but I want to trap any unforseen errors and make the trigger as robust as possible.
DDL triggers seem to work differently than normal T-SQL and doesn't seem to honour the TRY-CATCH block.
The following code works fine if it is in an SP but it doesn't work if it is in a DDL trigger.
BEGIN TRY
-- Simulate an error
RAISERROR ('Just a test!', 14, 1);
END TRY
BEGIN CATCH
DECLARE #errorNumber INT = ERROR_NUMBER()
DECLARE #errorMessage NVARCHAR(2048) = ERROR_MESSAGE() + '('
+ CAST(ERROR_NUMBER() AS NVARCHAR) + ')'
-- Log the error to the SQL server event log
EXEC master..xp_logevent 50005, #errorMessage, WARNING
END CATCH;
Any ideas?
You can't use xp logevent from triggers http://technet.microsoft.com/en-us/library/ms186244.aspx
Look at remarks for reasons
Is there any way to have a stored procedure automatically throw if any statement fails due to an error?
I'm inside a stored proc with a merge statement which can fail due to a primary key violation, however execution still continues.
Do I have to resort to if ##error != 0 throw ... everywhere?
EDIT: I'm using MS SQL Server 2012
EDIT: This seems to work, but is there a less verbose solution? It seems as if the introduction of try/catch makes flow jump to the catch block when an error is encountered. From there I just rethrow the exception.
begin try
....do lots of sql code
end try
begin catch
throw;
end catch
Use SET xact_abort ON at the beginning of the statement. It will cause an automatic rollback if any particular statement fails.
See What is the benefit of using "SET XACT_ABORT ON" in a stored procedure?.
Edit: the above is for SQL-Server.
How about wrapping it in a transaction so that if anything fails it will roll back any changes and you can have it return an error message.
Something like
BEGIN Transaction
--Do some code
if ##error > 0
BEGIN
--Do your throw here and then
ROLLBACK TRANSACTION
END
ELSE
BEGIN
COMMIT TRANSACTION
END
I have a SQL Server 2008 R2 stored procedure that runs a few INSERTs and UPDATEs in a TRANSACTION. After each statement, I need to log what just happened before doing the next step.
Here is my code:
BEGIN TRY
BEGIN TRANSACTION
INSERT INTO... -- 1st statement
INSERT INTO MyEventLog (EventDescription) VALUES ('Did Step 1') -- log
UPDATE... -- 2nd statement
INSERT INTO MyEventLog (EventDescription) VALUES ('Did Step 2') -- log
COMMIT TRANSACTION
END TRY
BEGIN CATCH
IF (##TRANCOUNT<>0) ROLLBACK TRANSACTION
EXEC LogError 'I got an error'
END CATCH
Problem is: if there is an error, the transaction rolls back all statements -- including the logging which I need. in the event of an error, how do I roll back the transactions but keep the logging.
I was going to ask why you would want to log an event that technically didn't happen, since the transaction would have been rolled back and the database would be in the state it was in before the transaction. But then it occurred to me that you probably just want to log it in order to know WHERE it failed so you can fix the underlying issue, which is a smart thing to do.
If that is indeed the case, the best thing to do is to rollback the entire transaction as you are currently doing, and to use your LogError SP to log the error message in another table. This is what I use:
CREATE PROCEDURE [dbo].[Error_Handler]
#returnMessage bit = 'False'
WITH EXEC AS CALLER
AS
BEGIN
DECLARE #number int,
#severity int,
#state int,
#procedure varchar(100),
#line int,
#message varchar(4000)
INSERT INTO Errors (Number,Severity,State,[Procedure],Line,[Message])
VALUES (
ERROR_NUMBER(),
ERROR_SEVERITY(),
ERROR_STATE(),
isnull(ERROR_PROCEDURE(),'Ad-Hoc Query'),
isnull(ERROR_LINE(),0),
ERROR_MESSAGE())
IF(#returnMessage = 'True')
BEGIN
select *
from Errors
where ErrorID = scope_identity()
END
END
The error message should let you know what went wrong in what table, and that should be enough info to fix the problem.
See Logging messages during a transaction. Is a bit convoluted:
use sp_trace_generateevent to generate the logged event
use event notifications to capture the custom trace event into a message
use internal activation to process the message and write it into the logging table
But it does allow you to log messages during a transaction and the messages will be persisted even if the transaction rolls back. Order of logging is preserved.
You also need to make your transaction and stored procedure play nice when one procedure fails but the transaction can continue (eg. when processing a batch and one item fails, you want to continue wit the rest of the batch). See Exception handling and nested transactions.
How about putting the logging statements into a separate transaction?
I'd put it down in the CATCH block:
BEGIN CATCH
IF (##TRANCOUNT<>0)
ROLLBACK TRANSACTION
EXEC LogError 'I got an error'
BEGIN TRANSACTION
INSERT INTO MyEventLog (EventDescription) VALUES ('Error Updating') -- log
END TRANSACTION
END CATCH
As it turns out, table variables don't obey transaction semantics. So, you could insert into a table variable and then insert from your table variable into your logging table after the catch block.