Try / Catch error_message() Fetch Multiple Errors - sql

I have about 1000 line stored procedure that make a complex stuff , i am trying to use try , catch to get any errors , it works fine but if the script contain multiple errors , only the last error is retrieved , so error_message() only get the last error
BEGIN TRY
Begin Tran
--Staff
Commit Tran
End Try
BEGIN CATCH
IF ##TRANCOUNT > 0
select
error_message() as errormessage,
error_number() as erronumber,
error_state() as errorstate,
error_procedure() as errorprocedure,
error_line() as errorline;
ROLLBACK
END CATCH
Error : 1
Error : 2
Error : 3
the result set of the select query , return only Error : 3
Is there any way to get all error , or at least the first error which is more important to be retrieved
I have sql server 2008 ,
I google before post here and can't find any applicable solution

Related

Try catch in trigger not suppressing error

I have a trigger and I want to surround the dml statements in the trigger by a try catch block so that any exception that occur in the trigger does not throw any exceptions outside the trigger. But the error is not suppressed.
My trigger is :
ALTER TRIGGER [dbo].[Deal.OnInsertUpdateAddDealAuditDetails]
ON [dbo].[Deal]
AFTER UPDATE, INSERT, DELETE
AS
BEGIN
BEGIN TRY
--SOME DML STATEMENTS
select 1/0
END TRY
BEGIN CATCH
SELECT
ERROR_NUMBER() AS ErrorNumber,
ERROR_MESSAGE() AS ErrorMessage;
END CATCH
END
The error output is:
Msg 3616, Level 16, State 1, Line 1
An error was raised during trigger execution. The batch has been aborted and the user transaction, if any, has been rolled back.
XACT_ABORT is implicitly ON inside triggers. It rolls back the current transaction when a Transact-SQL statement raises a run-time error.
You have to handle the exception in original Insert query to aboid throwing the error.
BEGIN TRY
INSERT INTO deal(col1,col2,..)
VALUES (val1,val2,..)
END TRY
BEGIN CATCH
SELECT Error_number() AS ErrorNumber,
Error_message() AS ErrorMessage;
END CATCH
Note : The inserted records will not be present in the table. If you want the inserted records to be present in table though the trigger failed then you may have to commit the transaction first inside the trigger

Update row entries using Transact-SQL

I have a SQL Server 2014 database called Database.Main which has columns Type and Code.
I need to modify an existing Transact-SQL script to select all the rows that have Type equal to MyObject.Main and change the Code to integer 1000.
How can I do this in Transact-SQL?
BEGIN TRY
BEGIN TRAN Update_Table;
COMMIT;
END TRY
BEGIN CATCH
print 'Error encountered updating entries'
print ERROR_MESSAGE()
rollback;
END CATCH
Answer
Based on the answer from Raul below the solution is this:
BEGIN TRY
BEGIN TRAN Update_Table;
UPDATE Database.Main
SET Code = 1000
WHERE Type = 'MyObject.Main';
COMMIT;
END TRY
BEGIN CATCH
print 'Error encountered updating entries'
print ERROR_MESSAGE()
rollback;
END CATCH
A simple UPDATE command would do?
update table_name
set Code = 1000
where Type = 'MyObject.Main';

COMMIT SQL TRANSACTION IN ROLLBACK

I have created stored procedure with TRY CATCH. In CATCH section i am executing another stored procedure to store error in table Error_Details.
Now in C#, I am using DbTransaction to commit and rollback depend upon error.
Problem is, while rollback changes table Error_Details also getting rolled back.
Is there any option where everything should rolled back but not Error_Details table?
Use the transactions inside the stored procedure itself.
Please see one sample case below.
BEGIN TRY
BEGIN TRANSACTION MyActivity
-- Your SQL COMMANDS
COMMIT TRANSACTION MyActivity
END TRY
BEGIN CATCH
IF (##TRANCOUNT > 0)
BEGIN
ROLLBACK TRANSACTION MyActivity
END
INSERT INTO ERROR_DETAILS --INSERTING ErrorInfo INTO LOG TABLE
SELECT
ERROR_NUMBER() AS ErrorNumber,
ERROR_SEVERITY() AS ErrorSeverity,
ERROR_STATE() AS ErrorState,
ERROR_PROCEDURE() AS ErrorProcedure,
ERROR_LINE() AS ErrorLine,
ERROR_MESSAGE() AS ErrorMessage
END CATCH

Anticipating fatal errors when using stored procedures

Please see the code below:
ALTER PROCEDURE GetPerson
AS
BEGIN TRANSACTION;
BEGIN TRY
DECLARE #TestVariable1 AS INT;
DECLARE #TestVariable2 AS INT;
SET #TestVariable1 = 1;
SET #TestVariable2 = 0;
DECLARE #TestVariable3 AS INT;
SET #TestVariable3 = #TestVariable1 / #TestVariable2;
PRINT 'transaction committed';
END TRY
BEGIN CATCH
ROLLBACK;
PRINT 'transaction rolled back';
END CATCH
I could run this using the command:
EXEC GetPerson
The transaction is successfully rolled back because you cannot divide my zero.
Now see the code below:
ALTER PROCEDURE GetPerson
AS
BEGIN TRANSACTION;
BEGIN TRY
CREATE TABLE #Test
(
test INT
);
SELECT TOP 1 *
FROM #Test;
SELECT TOP 1 *
FROM person.person;
DROP TABLE #Test;
SELECT TOP 1 *
FROM #Test;
PRINT 'transaction committed';
END TRY
BEGIN CATCH
ROLLBACK;
PRINT 'transaction rolled back';
END CATCH
If I run the same comment:
EXEC GetPerson
then I get an error:
Msg 208, Level 16, State 0, Procedure GetPerson, Line 11
Invalid object name '#Test'.
Msg 266, Level 16, State 2, Procedure GetPerson, Line 11
Transaction count after EXECUTE indicates a mismatching number of BEGIN and COMMIT statements. Previous count = 0, current count = 1
I understand that not all errors are caught by the TRY clause as explained here: https://msdn.microsoft.com/en-gb/library/ms178592.aspx
When RAISERROR is run with a severity of 11 or higher in a TRY block, it transfers control to the associated CATCH block
My question is: how do you deal with these? I know in my code it is a clear developer mistake to try to select from a temp table that no longer exists. However, I am wandering if there are other errors I should deal with.
Is it acceptable practice to do this when calling the stored procedure:
EXECUTE GetPerson ;
IF ##trancount >= 1
ROLLBACK;
Alternatively I could do something like this:
EXEC GetPerson
IF ##trancount >= 1
INSERT INTO dbLog ("Trancount greater than 1 after running GetPerson")
Error 208 is a compilation error. Your code never run, anything, because it could not even compile. This is the same as asking a C# program to catch a compile error.
Besides, if you want a correct try/catch block that handles exceptions and transaction, see Exception handling and nested transactions:
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
Obviously this will not handle syntax/binding errors (like 208), but at least will handle correct procedures. Remember that checking XACT_STATE() in the CATCH block is mandatory. Consider an error like 1205 (deadlock) where the CATCH block runs after the transaction was rolled back. You also need to honor and handle caller's transaction.

SQL Server transaction handling

I'm running the following stored procedure and I'm receiving the error
The COMMIT TRANSACTION request has no corresponding BEGIN TRANSACTION.
what am I missing here?
CREATE PROCEDURE spImportData
AS
BEGIN TRANSACTION
BEGIN TRY
SET IDENTITY_INSERT PINCDOCControlNew..tblActionType ON
END TRY
BEGIN CATCH
PRINT 'IDENTITY_INSERT IS ON'
END CATCH
GO
BEGIN TRY
INSERT INTO PINCDOCControlNew..tblActionType(ActionTypeID,ActionType,ActionTypeDescription)
SELECT ActionTypeID,ActionType,ActionTypeDescription
FROM PINCDOCControlOld..tblActionType
SET IDENTITY_INSERT PINCDOCControlNew..tblActionType OFF
END TRY
BEGIN CATCH
SET IDENTITY_INSERT PINCDOCControlNew..tblActionType OFF
EXECUTE usp_GetErrorInfo
END CATCH
BEGIN TRY
SET IDENTITY_INSERT PINCDOCControlNew..tblArea ON
END TRY
BEGIN CATCH
PRINT 'IDENTITY_INSERT IS ON'
END CATCH
GO
BEGIN TRY
INSERT INTO PINCDOCControlNew..tblArea(AreaID,AreaDescription,AreaNo)
SELECT AreaNo,AreaDescription,Area
FROM PINCDOCControlOld..tblArea
SET IDENTITY_INSERT PINCDOCControlNew..tblArea OFF
END TRY
BEGIN CATCH
SET IDENTITY_INSERT PINCDOCControlNew..tblArea OFF
EXECUTE usp_GetErrorInfo
END CATCH
IF ##ERROR <> 0
BEGIN
-- Rollback the transaction
ROLLBACK
-- Raise an error and return
RAISERROR ('Error in inserting.', 16, 1)
RETURN
END
COMMIT
I would try to have just a single BEGIN TRY .... END TRY block, in which you have all your logic that you want to execute. If anything goes wrong - anywhere in your logic - you'll be thrown into the BEGIN CATCH.... END CATCH block.
Start your transaction before your BEGIN TRY, and have the only COMMIT as the last statement in your TRY block - and in your CATCH block, have a rollback.
Something like this:
CREATE PROCEDURE dbo.spImportData
AS
BEGIN TRANSACTION
BEGIN TRY
SET IDENTITY_INSERT PINCDOCControlNew..tblActionType ON
INSERT INTO
PINCDOCControlNew..tblActionType(ActionTypeID, ActionType, ActionTypeDescription)
SELECT
ActionTypeID, ActionType, ActionTypeDescription
FROM
PINCDOCControlOld..tblActionType
SET IDENTITY_INSERT PINCDOCControlNew..tblActionType OFF
-- tblArea
SET IDENTITY_INSERT PINCDOCControlNew..tblArea ON
INSERT INTO
PINCDOCControlNew..tblArea(AreaID, AreaDescription, AreaNo)
SELECT
AreaNo, AreaDescription, Area
FROM
PINCDOCControlOld..tblArea
SET IDENTITY_INSERT PINCDOCControlNew..tblArea OFF
COMMIT TRANSACTION
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION
SET IDENTITY_INSERT PINCDOCControlNew..tblActionType OFF
SET IDENTITY_INSERT PINCDOCControlNew..tblArea OFF
EXECUTE usp_GetErrorInfo
RAISERROR ('Error in inserting.', 16, 1)
END CATCH
With this approach, you have exactly ONE BEGIN TRANSACTION, and either one single corresponding COMMIT TRANSACTION, or a single corresponding ROLLBACK TRANSACTION
I typically also add this SELECT statement to my CATCH block to get the error message and error code of what went wrong:
SELECT
ERROR_NUMBER() AS ErrorNumber,
ERROR_SEVERITY() AS ErrorSeverity,
ERROR_STATE() AS ErrorState,
ERROR_PROCEDURE() AS ErrorProcedure,
ERROR_LINE() AS ErrorLine,
ERROR_MESSAGE() AS ErrorMessage
what am i missing here?
You probably have a surplus rather than a deficit. You need to remove the GO statements.
The only sensible interpretation of your script is that it is all intended to be part of the spImportData stored procedure definition.
This ends at the first GO statement (used by client tools to delimit batches) and the remaining batches are executed immediately in auto commit transactions as no explicit BEGIN TRAN has been run. When the COMMIT statement is reached there is nothing to commit hence the error.