如何将Try/Catch添加到SQL存储过程

Dom*_*nic 39 sql t-sql sql-server try-catch

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
Run Code Online (Sandbox Code Playgroud)

在这个过程中如何处理try catch异常?

Pre*_*gha 43

看到这里

 CREATE PROCEDURE [dbo].[PL_GEN_PROVN_NO1]        
       @GAD_COMP_CODE  VARCHAR(2) =NULL, 
       @@voucher_no numeric =null output 
       AS         
   BEGIN  

     begin try 
         -- your proc code
     end try

     begin catch
          -- what you want to do in catch
     end catch    
  END -- proc end
Run Code Online (Sandbox Code Playgroud)

  • 我建议:添加 `@ERROR_MESSAGE()` 来获取 catch 块中实际需要的异常消息! (2认同)

Rem*_*anu 31

由于事务的复杂性增加,Transact-SQL比C#或C++尝试/捕获块更棘手.CATCH块必须检查xact_state()函数并确定它是否可以提交或必须回滚.我在博客中介绍了这个主题,并且有一篇文章介绍了如何使用try catch块正确处理事务,包括可能的嵌套事务:异常处理和嵌套事务.

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
Run Code Online (Sandbox Code Playgroud)


ash*_*awg 12

使用SQL存储过程进行错误处理

TRY/CATCH错误处理可以在过程内部或外部(或两者)进行.以下示例演示了两种情况下的错误处理.

如果要进一步试验,可以在Stack Exchange Data Explorer分叉查询.

(这使用临时存储过程 ......我们无法在SEDE上创建常规 SP,但功能相同.)

--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
Run Code Online (Sandbox Code Playgroud)

以下是按原样运行上述sql的结果:

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.
Run Code Online (Sandbox Code Playgroud)

¹从存储过程的CATCH块中取消注释"附加错误行" 将产生:

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
Run Code Online (Sandbox Code Playgroud)

²MAIN过程中取消注释"附加错误行" 将产生:

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)
Run Code Online (Sandbox Code Playgroud)

使用单个过程进行错误处理

关于存储过程和错误处理的主题,使用单个动态存储过程处理多个其他过程或代码段的错误可能会有所帮助(并且更整洁).

这是一个例子:

--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
Run Code Online (Sandbox Code Playgroud)

示例输出:(该查询可以在SEDE分叉此处.)

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.
Run Code Online (Sandbox Code Playgroud)

文档:

TRY/CATCH block 的范围内,可以使用以下系统函数来获取有关导致CATCH块执行的错误的信息:

(来源)

请注意,有两种类型的SQL错误:终端可捕获. TRY/ CATCH[显然]只能抓住"可捕获"错误.这是了解SQL错误的多种方法之一,但它可能是最有用的.

正如荷马所说,与以后相比,它"现在更好地失败"(在开发期间) ...