I just want to check if the place where I put the ##Error and Begin/Commit tran is correct?
I am unsure if I should you the Begin Tran over the DELETE statement instead? And does the ##ERROR make any sense at all?
Thanks!
CREATE PROCEDURE spDeleteAnInactiveEmployee
#TrainerID int,
#EActive char (1)
AS
BEGIN TRY
BEGIN TRAN
IF (SELECT COUNT(*) FROM EmployeeDetails ed
WHERE TrainerID = #TrainerID) = 0
RAISERROR ('Trainer details were not deleted. Trainer ID does not exist.', 16, 1)
IF EXISTS (SELECT * FROM EmployeeDetails ed
WHERE TrainerID = #TrainerID AND EActive = 'Y')
RAISERROR ('Trainer details were not deleted. Trainer is still active.', 16, 1)
DELETE FROM [EmployeeDetails]
WHERE TrainerID = #TrainerID AND EActive = 'N'
IF ##ERROR = 0
COMMIT TRAN
BEGIN
PRINT 'Employee ID' + CAST (#TrainerID AS VARCHAR) + ' was successfully deleted.'
END
END TRY
BEGIN CATCH
SELECT
ERROR_NUMBER() AS ErrorNumber,
ERROR_STATE() AS ErrorState,
ERROR_SEVERITY() AS ErrorSeverity,
ERROR_PROCEDURE() AS ErrorProcedure,
ERROR_LINE() AS ErrorLine,
ERROR_MESSAGE() AS ErrorMessage;
IF (XACT_STATE()) = -1
BEGIN
PRINT 'Transaction was not committed'
ROLLBACK TRANSACTION;
END;
IF (XACT_STATE()) = 1
BEGIN
PRINT 'Transaction was committed'
COMMIT TRANSACTION;
END;
END CATCH;
GO
##ERROR is unnecessary when you use TRY/CATCH. Before TRY/CATCH you had to check ##ERROR after each statement that might fail and use GOTO to force control flow to an error label.
So this should be something like:
CREATE PROCEDURE spDeleteAnInactiveEmployee
#TrainerID int,
#EActive char (1)
AS
BEGIN
SET XACT_ABORT ON;
BEGIN TRY
BEGIN TRAN
IF (SELECT COUNT(*) FROM EmployeeDetails ed
WHERE TrainerID = #TrainerID) = 0
RAISERROR ('Trainer details were not deleted. Trainer ID does not exist.', 16, 1)
IF EXISTS (SELECT * FROM EmployeeDetails ed
WHERE TrainerID = #TrainerID AND EActive = 'Y')
RAISERROR ('Trainer details were not deleted. Trainer is still active.', 16, 1)
DELETE FROM [EmployeeDetails]
WHERE TrainerID = #TrainerID AND EActive = 'N'
COMMIT TRAN
PRINT 'Employee ID' + CAST (#TrainerID AS VARCHAR) + ' was successfully deleted.'
END TRY
BEGIN CATCH
IF ##TRANCOUNT > 0 ROLLBACK;
THROW;
END CATCH;
END
Related
I want to know if both stored procedures below could perform well to do the same thing or if there is any major difference? I have tested them and both are working, but I don't know which one I should use.
I want to select just one of the procedures, if both would perform just fine, that's alright, I want to understand what major different could arise if they are indeed different.
I also want to ensure that the transaction will be rolled back if there is an error in the procedure.
Thanks!
CREATE PROCEDURE spDeleteAnInactiveEmployee
#TrainerID int,
#EActive char (1)
AS
BEGIN
SET XACT_ABORT ON;
BEGIN TRY
BEGIN TRAN
IF (SELECT COUNT(*) FROM EmployeeDetails ed
WHERE TrainerID = #TrainerID) = 0
RAISERROR ('Trainer details were not deleted. Trainer ID does not exist.', 16, 1)
IF EXISTS (SELECT * FROM EmployeeDetails ed
WHERE TrainerID = #TrainerID AND EActive = 'Y')
RAISERROR ('Trainer details were not deleted. Trainer is still active.', 16, 1)
DELETE FROM [EmployeeDetails]
WHERE TrainerID = #TrainerID AND EActive = 'N'
COMMIT TRAN
PRINT 'Employee ID' + CAST (#TrainerID AS VARCHAR) + ' was successfully deleted.'
END TRY
BEGIN CATCH
IF ##TRANCOUNT > 0 ROLLBACK;
THROW;
END CATCH;
END
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROC spDeleteAnInactiveEmployee
#TrainerID int,
#EActive char (1)
AS
BEGIN TRY
BEGIN TRAN
IF (SELECT COUNT(*) FROM EmployeeDetails ed
WHERE TrainerID = #TrainerID) = 0
RAISERROR ('Trainer details were not deleted. Trainer ID does not exist.', 16, 1)
IF EXISTS (SELECT * FROM EmployeeDetails ed
WHERE TrainerID = #TrainerID AND EActive = 'Y')
RAISERROR ('Trainer details were not deleted. Trainer is still active.', 16, 1)
DELETE FROM [EmployeeDetails]
WHERE TrainerID = #TrainerID AND EActive = 'N'
COMMIT TRAN
BEGIN
PRINT 'Employee ID' + CAST (#TrainerID AS VARCHAR) + ' was successfully deleted.'
END
END TRY
BEGIN CATCH
ROLLBACK
PRINT 'An error has occurred and the transaction was not committed'
SELECT
ERROR_NUMBER() AS ErrorNumber,
ERROR_STATE() AS ErrorState,
ERROR_SEVERITY() AS ErrorSeverity,
ERROR_PROCEDURE() AS ErrorProcedure,
ERROR_LINE() AS ErrorLine,
ERROR_MESSAGE() AS ErrorMessage;
END CATCH;
GO
In my case I want to stop exec of any further executions on all other nested transactions.
If there is an error in tran1 or tran 2 execution should stop and throw error an rollback all previous changes made.
SET XACT_ABORT ON;
BEGIN TRY
BEGIN TRANSACTION 1
Insert into Table1 Values (.....)
Delete from Table2
--
--other example code
--
COMMIT TRANSACTION 1
BEGIN TRANSACTION 2
ALTER TABLE [dbo].[Table1] NOCHECK CONSTRAINT [FK_Table2]
WHILE 1=1
BEGIN
WAITFOR DELAY '00:00:01'
DELETE TOP (1000) ufb
FROM Table1 ufb
INNER JOIN Table2 mbss on mbss.ID=ufb.ID
--
IF ##ROWCOUNT < 1 BREAK
END
--other example code
COMMIT TRANSACTION 2
BEGIN TRANSACTION 3
--Some insert code
--other example code
COMMIT TRANSACTION 3
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()
RAISERROR (#ErrorMessage,
#ErrorSeverity,
#ErrorState
);
END CATCH
I writed this trigger:
ALTER TRIGGER [dbo].[trg_abort_insert]
ON [dbo].[F_DOCCURRENTPIECE]
AFTER UPDATE
AS
BEGIN
SET NOCOUNT ON
SET XACT_ABORT ON
BEGIN TRY
BEGIN TRAN
IF EXISTS (SELECT *
FROM Inserted i
INNER JOIN Deleted d ON i.CBMARQ= d.CBMARQ
WHERE i.DC_Piece <> d.DC_Piece
AND i.DC_Domaine = 0
AND i.DC_IdCol = 6)
COMMIT TRAN
END TRY
BEGIN CATCH
SELECT ERROR_MESSAGE()
IF ##TRANCOUNT>0
ROLLBACK
END CATCH;
END
I'am having this error:
Transaction count after EXECUTE indicates a mismatching number of BEGIN and COMMIT statements. Previous count = 1, current count = 2
Pleaze help me
This is because you have nested transaction. I mean a transaction was open when trigger was fired. You need to modify your trigger to handle nested transaction like mentioned below :
create TRIGGER [dbo].[trg_abort_insert]
ON [dbo].[F_DOCCURRENTPIECE]
AFTER UPDATE
AS
BEGIN
SET NOCOUNT ON
SET XACT_ABORT ON
declare #trancount int = ##trancount
BEGIN TRY
if #trancount > 0
begin
save transaction t1
end
else
begin
begin transaction
end
IF EXISTS (SELECT *
FROM Inserted i
INNER JOIN Deleted d ON i.CBMARQ= d.CBMARQ
WHERE i.DC_Piece <> d.DC_Piece
AND i.DC_Domaine = 0
AND i.DC_IdCol = 6)
if #trancount = 0
begin
COMMIT TRAN
end
END TRY
BEGIN CATCH
if #trancount > 0
rollback transaction t1
if #trancount = 0
rollback transaction
SELECT ERROR_MESSAGE()
END CATCH;
end
This should work for you. Let me know if this helps.
According to this, you can have a state in the catch block where you can't do any write operations unless you rollback first.
This is an issue when you're attempting to handle nested transactions and do error logging. In the following example, the exception in the nested procedure gets lost and nothing is logged.
IF OBJECT_ID(N'dbo.ErrorLog', N'U') IS NOT NULL
DROP TABLE dbo.ErrorLog;
GO
CREATE TABLE dbo.ErrorLog (Error NVARCHAR(4000));
GO
IF OBJECT_ID(N'tempdb..#Caller') IS NOT NULL
BEGIN
DROP PROC #Caller;
END;
GO
CREATE PROCEDURE #Caller
AS
BEGIN
SET NOCOUNT ON;
SET XACT_ABORT ON;
DECLARE #transCount TINYINT = ##TRANCOUNT,
#returnCode INT,
#errorMessage NVARCHAR(4000),
#errorNumber INT;
BEGIN TRY
IF (#transCount = 0)
BEGIN
BEGIN TRAN;
END;
EXEC #returnCode = #Called;
IF (#returnCode <> 0)
BEGIN
RAISERROR(N'Error in Called. Caller returned an error', 16, -1);
END;
IF (#transCount = 0)
BEGIN
COMMIT TRAN;
END;
END TRY
BEGIN CATCH
IF ((#transCount = 0) AND (XACT_STATE() <> 0))
BEGIN
ROLLBACK TRAN;
END;
SELECT #errorMessage = ERROR_MESSAGE(),
#errorNumber = ERROR_NUMBER();
INSERT dbo.ErrorLog(Error) VALUES(#errorMessage); --only this logging happens
RAISERROR(N'Error in Caller.', 16, -1);
RETURN #errorNumber;
END CATCH;
RETURN;
END;
GO
IF OBJECT_ID(N'tempdb..#Called') IS NOT NULL
BEGIN
DROP PROC #Called;
END;
GO
CREATE PROC #Called
AS
BEGIN
SET NOCOUNT ON;
SET XACT_ABORT ON;
DECLARE #transCount TINYINT = ##TRANCOUNT,
#errorMessage NVARCHAR(4000),
#errorNumber INT;
BEGIN TRY
IF (#transCount = 0) --doesn't start tran, already in one
BEGIN
BEGIN TRAN;
END;
SELECT 1/0; --generate an error; this exception gets lost
IF (#transCount = 0)
BEGIN
COMMIT TRAN;
END;
END TRY
BEGIN CATCH
IF ((#transCount = 0) AND (XACT_STATE() <> 0)) --cannot rollback here because this didn't start the transaction
BEGIN
ROLLBACK TRAN;
END;
SELECT #errorMessage = ERROR_MESSAGE(),
#errorNumber = ERROR_NUMBER();
INSERT dbo.ErrorLog(Error) VALUES(#errorMessage); --doesn't happen because of uncommitable transaction; raises exception, caught in CATCH block of Caller
RAISERROR(N'Error in Called.', 16, -1); --this doesn't happen
RETURN #errorNumber; --nothing returned
END CATCH;
RETURN;
END
GO
EXEC dbo.#Caller;
GO
SELECT * FROM dbo.ErrorLog;
GO
The single error logged is just the uncommitable transaction exception. Is there any way to handle nested transactions, in a TRY..CATCH, and still log errors that actually occur?
This approach helped me accomplish what I needed. Basically, in the case of a doomed tran (XACT_STATE() = -1), the entire transaction is rolled back to allow error logging at both levels while raising just one exception at the end of execution.
IF OBJECT_ID(N'dbo.ErrorLog', N'U') IS NOT NULL
DROP TABLE dbo.ErrorLog;
GO
CREATE TABLE dbo.ErrorLog (Error NVARCHAR(4000));
GO
IF OBJECT_ID(N'tempdb..#Caller') IS NOT NULL
BEGIN
DROP PROC #Caller;
END;
GO
CREATE PROCEDURE #Caller
AS
BEGIN
SET NOCOUNT ON;
SET XACT_ABORT ON;
DECLARE #transCount TINYINT = ##TRANCOUNT,
#returnCode INT,
#errorMessage NVARCHAR(4000),
#errorNumber INT;
BEGIN TRY
IF (#transCount = 0)
BEGIN
BEGIN TRAN;
END;
EXEC #returnCode = #Called;
IF (#returnCode <> 0)
BEGIN
RAISERROR(N'Error in Called. Caller returned an error', 16, -1);
END;
IF (#transCount = 0)
BEGIN
COMMIT TRAN;
END;
END TRY
BEGIN CATCH
IF (((#transCount = 0) AND (XACT_STATE() <> 0)) OR (XACT_STATE() = -1))
BEGIN
ROLLBACK TRAN;
END;
SELECT #errorMessage = ERROR_MESSAGE(),
#errorNumber = ERROR_NUMBER();
INSERT dbo.ErrorLog(Error) VALUES(#errorMessage); --only this logging happens
RAISERROR(N'Error in Caller.', 16, -1);
RETURN #errorNumber;
END CATCH;
RETURN;
END;
GO
IF OBJECT_ID(N'tempdb..#Called') IS NOT NULL
BEGIN
DROP PROC #Called;
END;
GO
CREATE PROC #Called
AS
BEGIN
SET NOCOUNT ON;
SET XACT_ABORT ON;
DECLARE #transCount TINYINT = ##TRANCOUNT,
#errorMessage NVARCHAR(4000),
#errorNumber INT;
BEGIN TRY
IF (#transCount = 0) --doesn't start tran, already in one
BEGIN
BEGIN TRAN;
END;
SELECT 1/0; --generate an error; this exception gets lost
IF (#transCount = 0)
BEGIN
COMMIT TRAN;
END;
END TRY
BEGIN CATCH
IF (((#transCount = 0) AND (XACT_STATE() <> 0)) OR (XACT_STATE() = -1)) --rollback even though didn't start this tran because its doomed
BEGIN
ROLLBACK TRAN;
END;
SELECT #errorMessage = ERROR_MESSAGE(),
#errorNumber = ERROR_NUMBER();
INSERT dbo.ErrorLog(Error) VALUES(#errorMessage); --doesn't happen because of uncommitable transaction; raises exception, caught in CATCH block of Caller
RAISERROR(N'Error in Called.', 16, -1); --this doesn't happen
RETURN #errorNumber; --nothing returned
END CATCH;
RETURN;
END
GO
DECLARE #returnCode INT;
EXEC #returnCode = dbo.#Caller; --one exception raised
SELECT #returnCode AS 'returnCode'; --error code 50000 returned
SELECT * FROM dbo.ErrorLog; --both errors logged incl original exception
I have a stored procedure with a TRY CATCH statement and within that TRY CATCH I am calling another stored procedure which is throwing an error. An exception is thrown and caught however if the error is within the called stored procedure this is not shown in the ERROR_PROCEDURE() it is set to NULL. It seems the reason is due to Dynamic SQL being executed in the called stored procedure.
ALTER PROC dbo.MyError AS
BEGIN
SET NOCOUNT, XACT_ABORT ON;
BEGIN TRY
BEGIN TRAN
--do stuff here
--SQL CODE
SELECT 'HELLO' AS hello
--then call sproc
EXEC dbo.MyInnerError
COMMIT TRANSACTION
END TRY
BEGIN CATCH
IF (##TRANCOUNT > 0)
BEGIN
ROLLBACK TRANSACTION
END
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
END
GO
ALTER PROC dbo.MyInnerError AS
BEGIN
DECLARE #SQl nvarchar(50) = 'SELECT 1/0 as DYNAMIC_FAIL';
EXEC SP_EXECUTESQL #SQl;
END
EXEC dbo.MyError
GO
I have tried nesting the stored procedure in its own TRY CATCH but this leads to TRANSACTION ROLLBACK issues.
Is ERROR_PROCEDURE() NULL because it is out of scope? And is there a way to set this?
It seems the reason is due to Dynamic SQL being executed in called Stored procedure. Is there a way to handle this?
Edit based on updated question and comments
ERROR_PROCEDURE() will not return a procedure name for SQL executed via SP_EXECUTESQL. Logically, if it did, it would return 'SP_EXECUTESQL' :). See this Connect entry "TRY/CATCH: ERROR_PROCEDURE() does not report name of procedure if error occured in dynamic SQL", in particular this sentence in the response from Microsoft;
Since there is no name associated with the ad hoc SQL, ERROR_PROCEDURE
will return NULL for errors raised from the execution level of the ad
hoc SQL.
I knocked up a very quick test and it works for me (SQL Server 2012);
CREATE PROC dbo.MyError AS
BEGIN
SET NOCOUNT, XACT_ABORT ON;
BEGIN TRY
BEGIN TRAN
--do stuff here
--SQL CODE
--then call sproc
EXEC dbo.MyInnerError
COMMIT TRANSACTION
END TRY
BEGIN CATCH
IF (##TRANCOUNT > 0)
BEGIN
ROLLBACK TRANSACTION
END
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
END
GO
CREATE PROC dbo.MyInnerError AS
BEGIN
;THROW 51000, 'This is my only error.', 1;
END
GO
EXEC dbo.MyError
GO
Result is;
ErrorNumber ErrorSeverity ErrorState ErrorProcedure ErrorLine ErrorMessage
----------- ------------- ----------- ---------------- ----------- ------------------------
51000 16 1 MyInnerError 4 This is my only error.
The issue is that the sub-proc call to MyInnerError isn't failing; it is a call within MyInnerError that is failing but yet MyInnerError is completely successfully.
MyInnerError is completing successfully because you are not trapping the error and reporting a failure like you are doing in the outer proc via the TRY / CATCH structure.
That, and errors coming from Dynamic SQL will naturally never set ERROR_PROCEDURE().
All of your procs should have the TRY / CATCH structure and the CATCH block should use either RAISERROR or THROW (depending on what version of SQL Server you are using) so that you can bubble the error up to the calling scope.
DECLARE #InNestedTransaction BIT = 0;
BEGIN TRY
IF (##TRANCOUNT > 0)
BEGIN
SET #InNestedTransaction = 1;
END;
ELSE
BEGIN
BEGIN TRAN;
END;
... one or more SQL statements ...
COMMIT;
END TRY
BEGIN CATCH
IF (#InNestedTransaction = 0)
BEGIN
ROLLBACK;
END;
IF (ERROR_PROCEDURE() IS NULL)
BEGIN
DECLARE #ErrMessage NVARCHAR(4000) = ERROR_MESSAGE(),
#ErrState TINYINT = ERROR_STATE(),
#ErrSeverity TINYINT = ERROR_SEVERITY();
RAISERROR(#ErrMessage, #ErrSeverity, #ErrState);
RETURN;
END;
;THROW; -- introduced in SQL Server 2012
---- If using SQL Server 2008, replace the above (from "IF" through "THROW")
---- with the following.
-- DECLARE #ErrMessage NVARCHAR(4000) = ERROR_MESSAGE();
-- RAISERROR(#ErrMessage, 16, 1);
-- RETURN;
END CATCH;
The IF (ERROR_PROCEDURE() IS NULL) block is used to catch situations like this where there is no proc generating the error. Calling ;THROW; by itself will bubble-up the current error info, which in this case is NULL for ERROR_PROCEDURE().
If you want to test this yourself, just run the following SQL which creates 3 stored procedures, all of which use the structure shown above. The inner-most procedure (ErrorTest1) calls sp_executesql using SELECT 1/0; as the query. The "divide by zero" error is trapped by the TRY / CATCH. The ERROR_PROCEDURE() function returns NULL as it was Dynamic SQL that generated the error. So, RAISERROR is called (technically calling ;THROW 50505, #ErrMessage, #ErrState; would work as well) to indicate to the calling process that the current proc generated an error.
Test Setup:
IF (OBJECT_ID(N'ErrorTest3') IS NOT NULL)
BEGIN
DROP PROCEDURE ErrorTest3;
END;
IF (OBJECT_ID(N'ErrorTest2') IS NOT NULL)
BEGIN
DROP PROCEDURE ErrorTest2;
END;
IF (OBJECT_ID(N'ErrorTest1') IS NOT NULL)
BEGIN
DROP PROCEDURE ErrorTest1;
END;
GO
CREATE PROCEDURE dbo.ErrorTest1
AS
SET NOCOUNT ON;
DECLARE #InNestedTransaction BIT = 0;
BEGIN TRY
IF (##TRANCOUNT > 0)
BEGIN
SET #InNestedTransaction = 1;
END;
ELSE
BEGIN
BEGIN TRAN;
END;
SELECT '1a';
EXEC sp_executesql N'SELECT 1/0 AS [ForceError];';
SELECT '1b';
COMMIT;
END TRY
BEGIN CATCH
IF (#InNestedTransaction = 0)
BEGIN
ROLLBACK;
END;
IF (ERROR_PROCEDURE() IS NULL)
BEGIN
DECLARE #ErrMessage NVARCHAR(4000) = ERROR_MESSAGE(),
#ErrState TINYINT = ERROR_STATE(),
#ErrSeverity TINYINT = ERROR_SEVERITY();
RAISERROR(#ErrMessage, #ErrSeverity, #ErrState);
RETURN;
END;
;THROW; -- introduced in SQL Server 2012
END CATCH;
GO
CREATE PROCEDURE dbo.ErrorTest2
AS
SET NOCOUNT ON;
DECLARE #InNestedTransaction BIT = 0;
BEGIN TRY
IF (##TRANCOUNT > 0)
BEGIN
SET #InNestedTransaction = 1;
END;
ELSE
BEGIN
BEGIN TRAN;
END;
SELECT '2a';
EXEC dbo.ErrorTest1;
SELECT '2b';
COMMIT;
END TRY
BEGIN CATCH
IF (#InNestedTransaction = 0)
BEGIN
ROLLBACK;
END;
IF (ERROR_PROCEDURE() IS NULL)
BEGIN
DECLARE #ErrMessage NVARCHAR(4000) = ERROR_MESSAGE(),
#ErrState TINYINT = ERROR_STATE(),
#ErrSeverity TINYINT = ERROR_SEVERITY();
RAISERROR(#ErrMessage, #ErrSeverity, #ErrState);
RETURN;
END;
;THROW; -- introduced in SQL Server 2012
END CATCH;
GO
CREATE PROCEDURE dbo.ErrorTest3
AS
SET NOCOUNT ON;
DECLARE #InNestedTransaction BIT = 0;
BEGIN TRY
IF (##TRANCOUNT > 0)
BEGIN
SET #InNestedTransaction = 1;
END;
ELSE
BEGIN
BEGIN TRAN;
END;
SELECT '3a';
EXEC dbo.ErrorTest2;
SELECT '3b';
COMMIT;
END TRY
BEGIN CATCH
IF (#InNestedTransaction = 0)
BEGIN
ROLLBACK;
END;
SELECT ERROR_PROCEDURE() AS [ErrorProcedure],
ERROR_STATE() AS [ErrorState],
ERROR_SEVERITY() AS [ErrorSeverity];
IF (ERROR_PROCEDURE() IS NULL)
BEGIN
DECLARE #ErrMessage NVARCHAR(4000) = ERROR_MESSAGE(),
#ErrState TINYINT = ERROR_STATE(),
#ErrSeverity TINYINT = ERROR_SEVERITY();
RAISERROR(#ErrMessage, #ErrSeverity, #ErrState);
RETURN;
END;
;THROW; -- introduced in SQL Server 2012
END CATCH;
GO
Run Test:
EXEC dbo.ErrorTest3;
Returns:
5 result sets:
3a
2a
1a
<empty>
ErrorProcedure ErrorState ErrorSeverity
ErrorTest1 1 16
And in the "Messages" tab:
Msg 50000, Level 16, State 1, Procedure ErrorTest1, Line 36
Divide by zero error encountered.