Try-Catch with Stored Procedures in SQL server - sql-server

I have created a stored procedure with try-catch for deleting records.A message should be displayed if a record is deleted or if there is an error.The delete procedure works but the message is not being displayed.
Also,it asks me to input a value for #msg and #return when executing the procedure.
Here is my Stored Procedure code:
IF OBJECT_ID('[dbo].[usp_AttendanceDelete]') IS NOT NULL
BEGIN
DROP PROCEDURE [dbo].[usp_AttendanceDelete]
END
GO
CREATE PROCEDURE [dbo].[usp_AttendanceDelete]
#A_ID int,
#msg VARCHAR(50) OUTPUT,
#return INT OUTPUT
AS
BEGIN
SET NOCOUNT ON
BEGIN TRANSACTION
BEGIN TRY
DELETE
FROM [dbo].[Attendance]
WHERE [A_ID] = #A_ID
SET #msg = 'Attendance Deleted'
SET #return = 1
END TRY
BEGIN CATCH
--SELECT error_message() as error
SET #msg = 'Attendance Delete FAIL.'
SET #return = 0
GOTO fail_rollback
END CATCH
COMMIT TRANSACTION
RETURN
fail_rollback:
ROLLBACK TRANSACTION
RETURN
END
GO
Here is the code to execute the procedure:
EXECUTE [dbo].[usp_AttendanceDelete]
#A_ID ='234',
#msg='success',
#return ='1'

You can supply variables rather than values when calling the stored procedure and you need to remember to mark them as output also:
DECLARE #msg varchar(50)
DECLARE #return int
EXECUTE [dbo].[usp_AttendanceDelete]
#A_ID ='234',
#msg=#msg output,
#return=#return output
--Do something with msg/return

Try this Query below
IF OBJECT_ID('[dbo].[usp_AttendanceDelete]') IS NOT NULL
BEGIN
DROP PROCEDURE [dbo].[usp_AttendanceDelete]
END
GO
CREATE PROCEDURE [dbo].[usp_AttendanceDelete]
#A_ID int,
#msg VARCHAR(100) OUTPUT,
#return INT OUTPUT
AS
BEGIN TRANSACTION;
BEGIN TRY
IF EXISTS(SELECT 1 FROM [Attendance] WHERE [A_ID] = #A_ID)
Begin
DELETE
FROM [dbo].[Attendance]
WHERE [A_ID] = #A_ID
SELECT #msg = 'Attendance Deleted'
SELECT #return = 1
END
ELSE
Begin
SELECT #msg='IN Put Record doesn''t exists'
END
END TRY
BEGIN CATCH
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;
IF ##TRANCOUNT > 0
ROLLBACK TRANSACTION;
END CATCH;
IF ##TRANCOUNT > 0
COMMIT TRANSACTION;
GO
DECLARE #msg VARCHAR(100),#return INt
EXEC DBO.[usp_AttendanceDelete] 2,#msg OUT,#return OUT
SELECT #msg, #return

Related

Linked Server COMMIT TRANSACTION fail on TRY CATCH

i'm working with Linked Server, and this code working fine
BEGIN TRY
INSERT INTO [Dev].[dbo].tb_test (no) SELECT no from [MYLINKEDSERVER].[mydb].[dbo].tb_test
DELETE FROM [MYLINKEDSERVER].[mydb].[dbo].tb_test
END TRY
BEGIN CATCH
SELECT 'fail'
END CATCH
but when i use it with COMMIT TRANSACTION, like this
BEGIN TRY
BEGIN TRANSACTION
INSERT INTO [Dev].[dbo].tb_test (no) SELECT no from [MYLINKEDSERVER].[mydb].[dbo].tb_test
DELETE FROM [MYLINKEDSERVER].[mydb].[dbo].tb_test
COMMIT
END TRY
BEGIN CATCH
IF ##TRANCOUNT > 0
ROLLBACK
END CATCH
show error
OLE DB provider "SQLNCLI11" for linked server "MYLINKEDSERVER" returned message
"The partner transaction manager has disabled its support for
remote/network transactions.".
What is wrong?
The solution is to write a stored procedure on remote server with return value, and execute it locally:
BEGIN TRY
BEGIN TRANSACTION
INSERT INTO [Dev].[dbo].tb_test (no)
SELECT no
FROM [MYLINKEDSERVER].[mydb].[dbo].tb_test
DECLARE #returnvalue INT
EXEC #returnvalue = [MYLINKEDSERVER].[mydb].[dbo].sp_update #no
IF #returnvalue = 1
BEGIN
COMMIT
END
ELSE
BEGIN
ROLLBACK
END
END TRY
BEGIN CATCH
IF ##TRANCOUNT > 0
ROLLBACK
END CATCH
The stored procedure:
CREATE PROCEDURE [dbo].[sp_update]
#no NVARCHAR(20)
AS
BEGIN
SET NOCOUNT ON
BEGIN TRY
BEGIN TRANSACTION
DELETE FROM mytb
WHERE no = #no
COMMIT
RETURN 1
END TRY
BEGIN CATCH
IF ##TRANCOUNT > 0
ROLLBACK
RETURN 0
END CATCH
END
Hope help someone
Try executing below command on Server on which you are going to do insert operation.
EXEC sp_serveroption #server = 'ReadServerName',#optname = 'remote proc transaction promotion', #optvalue = 'false' ;
Also try executing below code. I have added BEGIN DISTRIBUTED TRANSACTION instead of BEGIN TRANSACTION.
BEGIN TRY
BEGIN DISTRIBUTED TRANSACTION
INSERT INTO [Dev].[dbo].tb_test (no) SELECT no from [MYLINKEDSERVER].[mydb].[dbo].tb_test
DELETE FROM [MYLINKEDSERVER].[mydb].[dbo].tb_test
COMMIT
END TRY
BEGIN CATCH
IF ##TRANCOUNT > 0
ROLLBACK
END CATCH
Details are available at MS site.
Try this it should work
DECLARE #ServerName SYSNAME
, #Message nvarchar(1000)
, #CMD1 nvarchar(max)
--
DECLARE #Server_List Table
( SrvID SMALLINT
, SrvName SYSNAME )
--
Set NoCount ON
--
-- Load up linked server list
--
BEGIN
INSERT INTO #Server_List (SrvID, SrvName)
SELECT SrvID
, SrvName
FROM [master].[SYS].sysservers
ORDER BY SrvID ASC
END
--
SELECT TOP 1 #ServerName = SrvName
FROM #Server_List
ORDER BY SrvID ASC
--
-- Loop through the Linked Server List
--
WHILE EXISTS ( SELECT * FROM #Server_List )
BEGIN
SELECT #Message = 'Server Name is '+ #ServerName
--
RAISERROR (#Message, 10,1) WITH NOWAIT
--
SET #CMD1 = 'EXEC master.dbo.sp_serveroption #server=N'''
+ #ServerName
+ ''', #optname=N''rpc'', #optvalue=N''true'''
Exec sp_executesql #cmd1
--
SET #CMD1 = 'EXEC master.dbo.sp_serveroption #server=N'''
+ #ServerName
+ ''', #optname=N''rpc out'', #optvalue=N''true'''
Exec sp_executesql #cmd1
--
set #cmd1 = 'EXEC master.dbo.sp_serveroption #server = '''
+ #ServerName
+ ''', #optname=N''remote proc transaction promotion'', #optvalue=N''false'''
Exec sp_executesql #stmt=#cmd1,#params=N''
--
DELETE FROM #Server_List WHERE SrvName = #ServerName
--
SELECT TOP 1 #ServerName = SrvName
FROM #Server_List
ORDER BY SrvID ASC
--
END

How to select two record set only if second one was successful?

I am going to report result of a stored procedure (whether it was successful or has error) using a simple select statement before sending prepared record sets. so I just simply insert this select statement before sending real records sets. But even when I wrap these two select statement in a transaction to make them atomic yet if second select statement raises an error the first select executed and gives 'ok' and 'error' at the same time. here is the code:
CREATE PROCEDURE my_procedure
#id INT = NULL
AS
BEGIN
SET NOCOUNT ON;
SET XACT_ABORT ON;
BEGIN TRY
BEGIN TRANSACTION;
SELECT 1 AS [status], 'OK' AS [message];
SELECT 1/0;
COMMIT;
END TRY
BEGIN CATCH
ROLLBACK;
SELECT 0 AS [status], ERROR_MESSAGE() AS [message];
END CATCH;
END;
How could first select statement be done only if the the second statement is successful?
Maybe declare a couple of variables outsite of the TRY/CATCH. Then change their values in the CATCH if an error is thrown. After the TRY/CATCH, show the values of the variables.
ALTER PROCEDURE my_procedure
#id INT = NULL
AS
BEGIN
SET NOCOUNT ON;
SET XACT_ABORT ON;
DECLARE #status BIT = 1; --set status variable here
DECLARE #message VARCHAR(MAX) = 'OK'; --set message variable here
BEGIN TRY
BEGIN TRANSACTION;
SELECT 1/0;
COMMIT;
END TRY
BEGIN CATCH
ROLLBACK;
SET #status = 0; --change value of #status in the CATCH block
SET #message = ERROR_MESSAGE();--change value of #message in the CATCH block
END CATCH;
--show the value of each variable
SELECT #status AS 'Status',#message AS 'Message'
END;
Have you tried using RAISERROR() or THROW()? Specifically, using RAISERROR() with a severity of 11-19 will force the execution to jump to the CATCH block. See details at https://learn.microsoft.com/en-us/sql/t-sql/language-elements/raiserror-transact-sql.
Specifically take a look at Example 1:
BEGIN TRY
-- RAISERROR with severity 11-19 will cause execution to
-- jump to the CATCH block.
RAISERROR ('Error raised in TRY block.', -- Message text.
16, -- Severity.
1 -- State.
);
END TRY
BEGIN CATCH
DECLARE #ErrorMessage NVARCHAR(4000);
DECLARE #ErrorSeverity INT;
DECLARE #ErrorState INT;
SELECT
#ErrorMessage = ERROR_MESSAGE(),
#ErrorSeverity = ERROR_SEVERITY(),
#ErrorState = 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, -- Message text.
#ErrorSeverity, -- Severity.
#ErrorState -- State.
);
END CATCH;

Uncommitable transaction prevents error logging in nested transaction

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

ERROR_PROCEDURE() returning NULL for code executed by SP_EXECUTESQL

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.

How to know TSQL Stored Procedure Update Executed

How can I check if my TSQL stored procedure updated within the stored procedure in order to create a proper message?
Example:
ALTER PROCEDURE [dbo].[pUpdate]
#id uniqueidentifier,
#status int,
#message VARCHAR(100) OUTPUT
AS
BEGIN
SET NOCOUNT ON;
UPDATE [database].[dbo].[user]
SET status = #status
WHERE Id = #id
END
IF (SUCCESSFUL)
BEGIN
#message = 'Success!'
END
What are some possible ways to check if successful without using the parameters again?
This is what I currently use:
SELECT COUNT(*)
WHERE status = #status AND id = #id
Are there any other ways? I want to know for my knowledge and reference. Thanks.
Have you checked out ##ROWCOUNT? Might be what you're looking for (see this for details: http://technet.microsoft.com/en-us/library/ms187316.aspx). Basically it returns the number of rows affected by the last statement. I'd imagine if it were not "successful", it would be zero rows.
ALTER PROCEDURE [dbo].[pUpdate]
#id uniqueidentifier,
#status int,
#message VARCHAR(100) OUTPUT
AS
BEGIN
SET NOCOUNT ON;
UPDATE [database].[dbo].[user]
SET status = #status
WHERE Id = #id
END
IF (##ROWCOUNT > 0)
BEGIN
#message = 'Success!'
END
ELSE
BEGIN
#message = 'Not success!'
END
You can use a try catch block and log the success or failure to a table.
BEGIN TRY
BEGIN TRANSACTION
-- Add Your Code Here
-- Log Success to a log table
COMMIT
END TRY
BEGIN CATCH
-- Log failure to a log table
ROLLBACK
END CATCH
I would use ##ERROR system variable to check whether the last sentence was successfull (error # = 0) or not (error # > 0 ):
USE Database;
GO
BEGIN
UPDATE TableName
SET ColumnA = 4
WHERE ColumnB = 1;
END
IF (##ERROR = 0)
BEGIN
PRINT N'Successfull Update';
GO
END
You can go deeper into Microsoft MSDN here: http://technet.microsoft.com/es-es/library/ms188790.aspx
ALTER PROCEDURE [dbo].[pUpdate]
#id uniqueidentifier,
#status int,
#message VARCHAR(100) OUTPUT
AS
BEGIN
SET NOCOUNT ON;
UPDATE [database].[dbo].[user]
SET status = #status
WHERE Id = #id
END
IF (##ROWCOUNT > 0)
BEGIN
SELECT #message = 'Success!'
END
ELSE
BEGIN
SELECT #message = 'Not success!'
END

Resources