[sql] How to add a Try/Catch to SQL Stored Procedure

CREATE PROCEDURE [dbo].[PL_GEN_PROVN_NO1]        
@GAD_COMP_CODE  VARCHAR(2) =NULL, 
@@voucher_no numeric =null output 
AS         
BEGIN  
    DECLARE @NUM NUMERIC 
    DECLARE @PNO  NUMERIC                               
    SET @PNO = 0 
    DECLARE @PNO1 NUMERIC
    SET @PNO1=0 

--  begin transaction 

    IF NOT EXISTS (select GLDC_NEXT_PRV_NO
               FROM   GLAS_FINANCIAL_DOCUMENTS          
                   WHERE  GLDC_COMP_CODE  = @GAD_COMP_CODE        
                   AND GLDC_DOC_CODE  = 'JV' )
    BEGIN
               RAISERROR ('Error in generating provision number..',16,1) 
               -- ROLLBACK TRANSACTION
    END
ELSE
SELECT @PNO=ISNULL(GLDC_NEXT_PRV_NO,0)+1
FROM   GLAS_FINANCIAL_DOCUMENTS          
WHERE  GLDC_COMP_CODE  = @GAD_COMP_CODE        
AND GLDC_DOC_CODE  = 'JV' 

UPDATE  GLAS_FINANCIAL_DOCUMENTS        
SET GLDC_NEXT_PRV_NO = @PNO         
WHERE  GLDC_COMP_CODE  = @GAD_COMP_CODE        
AND GLDC_DOC_CODE  = 'JV' 

set @@VOUCHER_NO=@PNO    
--commit transaction 
END

In this proc how can I handle try catch for exception?

This question is related to sql sql-server tsql try-catch

The answer is


Create Proc[usp_mquestions]  
( 
 @title  nvarchar(500),   --0
 @tags  nvarchar(max),   --1
 @category  nvarchar(200),   --2
 @ispoll  char(1),   --3
 @descriptions  nvarchar(max),   --4
)              
 AS  
 BEGIN TRY




BEGIN
DECLARE @message varchar(1000); 
DECLARE @tempid bigint; 

IF((SELECT count(id) from  [xyz] WHERE title=@title)>0)
BEGIN
SELECT 'record already existed.';
END
ELSE
BEGIN               


if @id=0 
begin 
select @tempid =id from [xyz] where id=@id;

if @tempid is null 
BEGIN 
        INSERT INTO xyz
        (entrydate,updatedate)
        VALUES
        (GETDATE(),GETDATE())

        SET @tempid=@@IDENTITY;
 END 
END 
ELSE 
BEGIN 
set @tempid=@id 
END 
if @tempid>0 
BEGIN 

    -- Updation of table begin--


UPDATE  tab_questions
set title=@title, --0 
 tags=@tags, --1 
 category=@category, --2 
 ispoll=@ispoll, --3 
 descriptions=@descriptions, --4 
 status=@status, --5

WHERE id=@tempid ; --9 ;


IF @id=0 
BEGIN 
SET @message= 'success:Record added successfully:'+ convert(varchar(10), @tempid)
END 
ELSE 
BEGIN 
SET @message= 'success:Record updated successfully.:'+ convert(varchar(10), @tempid)

END 
END 
ELSE 
BEGIN 
SET @message= 'failed:invalid request:'+convert(varchar(10), @tempid)
END 

END
END

END TRY
BEGIN CATCH
    SET @message='failed:'+ ERROR_MESSAGE();
END CATCH
SELECT @message;

Error-Handling with SQL Stored Procedures

TRY/CATCH error handling can take place either within or outside of a procedure (or both). The examples below demonstrate error handling in both cases.

If you want to experiment further, you can fork the query on Stack Exchange Data Explorer.

(This uses a temporary stored procedure... we can't create regular SP's on SEDE, but the functionality is the same.)

--our Stored Procedure
create procedure #myProc as --we can only create #temporary stored procedures on SEDE. 
  begin
    BEGIN TRY
      print 'This is our Stored Procedure.'
      print 1/0                          --<-- generate a "Divide By Zero" error.
      print 'We are not going to make it to this line.'
    END TRY

    BEGIN CATCH
      print 'This is the CATCH block within our Stored Procedure:'
          + ' Error Line #'+convert(varchar,ERROR_LINE())
          + ' of procedure '+isnull(ERROR_PROCEDURE(),'(Main)')
      --print 1/0                        --<-- generate another "Divide By Zero" error.
        -- uncomment the line above to cause error within the CATCH ¹ 
    END CATCH
  end
go

--our MAIN code block:
BEGIN TRY
  print 'This is our MAIN Procedure.'
  execute #myProc  --execute the Stored Procedure
      --print 1/0                        --<-- generate another "Divide By Zero" error.
        -- uncomment the line above to cause error within the MAIN Procedure ²
  print 'Now our MAIN sql code block continues.'
END TRY

BEGIN CATCH
  print 'This is the CATCH block for our MAIN sql code block:'
          + ' Error Line #'+convert(varchar,ERROR_LINE())
          + ' of procedure '+isnull(ERROR_PROCEDURE(),'(Main)')
END CATCH

Here's the result of running the above sql as-is:

This is our MAIN Procedure.
This is our Stored Procedure.
This is the CATCH block within our Stored Procedure: Error Line #5 of procedure #myProc
Now our MAIN sql code block continues.

¹ Uncommenting the "additional error line" from the Stored Procedure's CATCH block will produce:

This is our MAIN procedure.
This is our Stored Procedure.
This is the CATCH block within our Stored Procedure: Error Line #5 of procedure #myProc
This is the CATCH block for our MAIN sql code block: Error Line #13 of procedure #myProc

² Uncommenting the "additional error line" from the MAIN procedure will produce:

This is our MAIN Procedure.
This is our Stored Pprocedure.
This is the CATCH block within our Stored Procedure: Error Line #5 of procedure #myProc
This is the CATCH block for our MAIN sql code block: Error Line #4 of procedure (Main)

Use a single procedure for error handling

On topic of stored procedures and error handling, it can be helpful (and tidier) to use a single, dynamic, stored procedure to handle errors for multiple other procedures or code sections.

Here's an example:

--our error handling procedure
create procedure #myErrorHandling as
  begin
    print ' Error #'+convert(varchar,ERROR_NUMBER())+': '+ERROR_MESSAGE()  
    print ' occurred on line #'+convert(varchar,ERROR_LINE())
         +' of procedure '+isnull(ERROR_PROCEDURE(),'(Main)')
    if ERROR_PROCEDURE() is null       --check if error was in MAIN Procedure
      print '*Execution cannot continue after an error in the MAIN Procedure.'
  end
go

create procedure #myProc as     --our test Stored Procedure
  begin
    BEGIN TRY
      print 'This is our Stored Procedure.'
      print 1/0                       --generate a "Divide By Zero" error.
      print 'We will not make it to this line.'
    END TRY
    BEGIN CATCH
     execute #myErrorHandling
    END CATCH
  end
go

BEGIN TRY                       --our MAIN Procedure
  print 'This is our MAIN Procedure.'
  execute #myProc                     --execute the Stored Procedure
  print '*The error halted the procedure, but our MAIN code can continue.'
  print 1/0                           --generate another "Divide By Zero" error.
  print 'We will not make it to this line.'
END TRY
BEGIN CATCH
  execute #myErrorHandling
END CATCH

Example Output: (This query can be forked on SEDE here.)

This is our MAIN procedure.
This is our stored procedure.
 Error #8134: Divide by zero error encountered.
 occurred on line #5 of procedure #myProc
*The error halted the procedure, but our MAIN code can continue.
 Error #8134: Divide by zero error encountered.
 occurred on line #5 of procedure (Main)
*Execution cannot continue after an error in the MAIN procedure.

Documentation:

In the scope of a TRY/CATCH block, the following system functions can be used to obtain information about the error that caused the CATCH block to be executed:

  • ERROR_NUMBER() returns the number of the error.
  • ERROR_SEVERITY() returns the severity.
  • ERROR_STATE() returns the error state number.
  • ERROR_PROCEDURE() returns the name of the stored procedure or trigger where the error occurred.
  • ERROR_LINE() returns the line number inside the routine that caused the error.
  • ERROR_MESSAGE() returns the complete text of the error message. The text includes the values supplied for any substitutable parameters, such as lengths, object names, or times.

(Source)

Note that there are two types of SQL errors: Terminal and Catchable. TRY/CATCH will [obviously] only catch the "Catchable" errors. This is one of a number of ways of learning more about your SQL errors, but it probably the most useful.

It's "better to fail now" (during development) compared to later because, as Homer says . . .


Transact-SQL is a bit more tricky that C# or C++ try/catch blocks, because of the added complexity of transactions. A CATCH block has to check the xact_state() function and decide whether it can commit or has to rollback. I have covered the topic in my blog and I have an article that shows how to correctly handle transactions in with a try catch block, including possible nested transactions: 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) ;
        return;
    end catch   
end

yep - you can even nest the try catch statements as:

BEGIN TRY
SET @myFixDte = CONVERT(datetime, @myFixDteStr,101)
END TRY
BEGIN CATCH
    BEGIN TRY
        SET @myFixDte = CONVERT(datetime, @myFixDteStr,103)
END TRY
BEGIN CATCH
    BEGIN TRY
        SET @myFixDte = CONVERT(datetime, @myFixDteStr,104)
    END TRY
    BEGIN CATCH
        SET @myFixDte = CONVERT(datetime, @myFixDteStr,105)
    END CATCH
END CATCH END CATCH

Examples related to sql

Passing multiple values for same variable in stored procedure SQL permissions for roles Generic XSLT Search and Replace template Access And/Or exclusions Pyspark: Filter dataframe based on multiple conditions Subtracting 1 day from a timestamp date PYODBC--Data source name not found and no default driver specified select rows in sql with latest date for each ID repeated multiple times ALTER TABLE DROP COLUMN failed because one or more objects access this column Create Local SQL Server database

Examples related to sql-server

Passing multiple values for same variable in stored procedure SQL permissions for roles Count the Number of Tables in a SQL Server Database Visual Studio 2017 does not have Business Intelligence Integration Services/Projects ALTER TABLE DROP COLUMN failed because one or more objects access this column Create Local SQL Server database How to create temp table using Create statement in SQL Server? SQL Query Where Date = Today Minus 7 Days How do I pass a list as a parameter in a stored procedure? SQL Server date format yyyymmdd

Examples related to tsql

Passing multiple values for same variable in stored procedure Count the Number of Tables in a SQL Server Database Change Date Format(DD/MM/YYYY) in SQL SELECT Statement Stored procedure with default parameters Format number as percent in MS SQL Server EXEC sp_executesql with multiple parameters SQL Server after update trigger How to compare datetime with only date in SQL Server Text was truncated or one or more characters had no match in the target code page including the primary key in an unpivot Printing integer variable and string on same line in SQL

Examples related to try-catch

Try-catch block in Jenkins pipeline script Error handling with try and catch in Laravel sql try/catch rollback/commit - preventing erroneous commit after rollback IsNumeric function in c# Why is "except: pass" a bad programming practice? EOFException - how to handle? How to efficiently use try...catch blocks in PHP How to return a value from try, catch, and finally? Multiple try codes in one block Catch KeyError in Python