我用的是SQL SERVER 2000 企业版。在一个存储过程中,例如我写了一句INSERT语句,但是由于unique索引的冲突或其它原因可能会导致此INSERT语句执行失败,当然也可能是UPDATE语句。我怎么样捕获这个错误呢?
这个存储过程是在上面的应用(VC,通过ADO(ODBC))调用的。有没有类似try...catch...之类的语句?
我用:
IF @@ERROR<>0
好像不行。另外,如果一个存储过程中有三句INSERT语句,如果第一条INSERT语句因为UNIQUE索引或其它原因导致失败了,下面的两条INSERT会不会继续执行?(也是在VC中通过ODBC调用)。存储过程中最通用的错误处理是怎么个写法?

解决方案 »

  1.   

    2005才有try...catch...2000的用@@error也能捕获吧
      

  2.   

    'sql中存储过程的错误处理功能很弱,不过可以根据实际情况适当建立触发器来实现错误处理'
    --下面演示了SQL错误处理的脆弱性    
        
      --演示1   
      --测试的存储过程1   
      create   proc   p1   
      as   
      print   12/0   
      if   @@error<>0   
      print   '发生错误1'   
        
      select   *   from   newid()   
      if   @@error<>0   
      print   '发生错误2'   
      go   
        
      --调用   
      exec   p1   
      go   
        
      --删除测试   
      drop   proc   p1   
        
      /*--测试结果   
        
      服务器:   消息   8134,级别   16,状态   1,过程   p1,行   6   
      遇到被零除错误。   
      发生错误1   
      服务器:   消息   208,级别   16,状态   1,过程   p1,行   10   
      对象名   'newid'   无效。   
        
      --*/   
        
        
      /*--结论1:   
        
      错误1,不是严重的错误,所以SQL会执行下去   
      错误2,属于严重的错误,所以SQL没有执行下去,因为没有第二个print的结果   
        
      --*/   
     --演示2,存储过程嵌套调用中的错误   
        
      --测试的存储过程1   
      create   proc   p1   
      as   
      print   12/0   
      if   @@error<>0   
      print   '发生错误1'   
        
      select   *   from   newid()   
      if   @@error<>0   
      print   '发生错误2'   
      go   
        
      --测试的存储过程2   
      create   proc   p2   
      as   
      exec   p1   
        
      if   @@error<>0   
      print   '调用   存储过程1   异常结束'   
      else   
      print   '调用   存储过程1   正常结束'   
      go   
        
      --调用   
      exec   p2   
      go   
        
      --删除测试   
      drop   proc   p1,p2   
        
      /*--测试结果   
        
      服务器:   消息   8134,级别   16,状态   1,过程   p1,行   8   
      遇到被零除错误。   
      发生错误1   
      服务器:   消息   208,级别   16,状态   1,过程   p1,行   12   
      对象名   'newid'   无效。   
      调用   存储过程1   异常结束   
      --*/   
        
        
      /*--结论2:   
        
      被调用的存储过程发生严重错误时,调用它的存储过程可以捕获错误,并可以继续执行下去   
      --*/   
    --演示3,更严重的错误,无法用   set   xact_abort   on   来自动回滚事务   
      set   xact_abort   on --我们希望能自动回滚事务   
      begin   tran   
      create   table   #t(id   int)   
      insert   #t   select   1   
      select   *   from   newid()   
      commit   tran   
      go   
        
      select   *   from   #t   
      rollback   tran   
      /*--测试结果   
        
        
      (所影响的行数为   1   行)   
        
      服务器:   消息   208,级别   16,状态   1,行   5   
      对象名   'newid'   无效。   
        
      id                       
      -----------     
      1   
        
      (所影响的行数为   1   行)   
      --*/   
        
      /*--结论3:   
        
      我们希望   set   xact_abort   on   可以实现出错时自动回滚事务   
      但结果令我们希望,出错时,事务并没有被回滚   
      因为我们查询到了#t的结果,而且最后的回滚语句也并没有报错   
      --*/
      

  3.   

    2000中只能捕捉一些简单的错误 
    到2005以后有TRY...CATCH 可以捕捉一些异常具体用法类似:
    ----try...catch用法
    begin try
       select 1/0
    end try
    begin catch 
       print'打印错误'
    end catch---raiserror的用法
    begin try
      raiserror('生成一个错误消息',11,1)
    end try
    begin catch
      select error_message() as 错误消息,
             error_severity() as严重级别,
             error_state() as state;
    end catch---使用sp_addmessage存储过程添加自定义错误
    exec sp_addmessage 50001,15, N'new user defined error message!',us_english
    exec sp_addmessage 50001,15, N'新增用户自定义错误消息!'
    exec sp_dropmessage 50001,'all'---使用sp_altermessage存储过程修改用户定义错误消息
    exec sp_altermessage 50001,'with_log',true'
      

  4.   

    那么在SQL SERVER 2000中,我必须每执行一条有可能发行错误的SQL语句就判断一下@@ERROR,如果出错就返回?
    IF @@ERROR=0 是不是永远返回TRUE?
    因为据说IF会清空@@ERROR?有没有更好的方法?
      

  5.   

    Begin TranInsert table1(....) valuse(...)
    if @@Error <>0
    begin
       roll back tran
       select retCode=0
    end Insert table2(....) valuse(...)
    if @@Error <>0
    begin
       roll back tran
       select retCode=0
    end 
      

  6.   


    lz可以考虑改变一下策略,先执行update,判断
    if @@rowcount=0 
    begin
    insert into
    end
      

  7.   

    你可以通过事务控制+变量叠加判断。
    如:
    declare @error int
    begin tran
       begin
         update t1 set a=1
         set @error=@@error
         update t2 set a=2
         set @error=@error+@@error
         .....
       end
    if @error=0
    commit
    else 
    rollback
       
      

  8.   

    楼上的是个好方法。
    有没有更好的呢?一句失败的delete(因为该表的主键在其它表中存在外键关联)之后的语句会不会继续执行,例如IF @@ERROR?
      

  9.   

    我看SQL SERVER 的帮助里面有一个例子是这样写的:
    IF (@@ERROR <> 0)
       SET @ErrorSave = @@ERROR但是我自己写了一个DELETE,这个DELETE会失败,在DELETE下面我加上:
    IF (@@ERROR <> 0)
    BEGIN
       SET @ErrorSave = @@ERROR
       PRINT @ErrorSave
    END打印出来的一直是0,看来必须这样:
    delete ...
    SET @ErrorSave = @@ERROR
    IF @ErrorSave <> 0)