存储过程错误处理

时间:2009-11-28 11:05:52

标签: sql-server tsql stored-procedures exception-handling

i have this procedure for inserting rows in tables(sql server 2005)

CREATE PROCEDURE ans_insert
    (
    @q_desc varchar(2000),
    @sub_id int,
    @marks int,
    @ans1 varchar(1000),
    @ans varchar(1000),
    @userid varchar(15),
    @cr_date datetime

    )
    AS
    BEGIN
    BEGIN TRY
    BEGIN TRANSACTION
        DECLARE @q_id int

        insert into questions(q_desc,sub_id,marks,created_by,DT_created) values(@q_desc,@sub_id,@marks,@userid,@cr_date);
        SET @q_id = IDENT_CURRENT('questions')

        INSERT INTO answers(ans_desc,q_id,created_by,DT_created,istrue)
            VALUES( @ans1,@q_id,@userid,@cr_date,
            CASE WHEN @ans1 =@ans THEN 1 ELSE 0 END);
    COMMIT TRANSACTION

    END TRY
    BEGIN CATCH

        DECLARE @ErrorMessage NVARCHAR(4000);
        DECLARE @ErrorSeverity INT;
        DECLARE @ErrorState INT;
        DECLARE @ErrorLine INT;
        SELECT @ErrorMessage = ERROR_MESSAGE(),
        @ErrorSeverity = ERROR_SEVERITY(),
        @ErrorLine=ERROR_LINE(),
        @ErrorState = ERROR_STATE();
        IF @@TRANCOUNT > 0 


    ROLLBACK TRANSACTION
        RAISERROR (@ErrorMessage,@ErrorSeverity,@ErrorState,@ErrorLine);
    END CATCH
    END

我从我的ASP.NET表单中将其称为

在添加所有参数后,AnsCmd是我的存储过程命令

               try
                {


                    conn.Open();

                    AnsCmd.ExecuteNonQuery();

                    lblMsg.Visible = true;
                    lblMsg.Text = "success";
                    conn.Close();
                }
                catch (SqlException sqlex)
                {
                    lblMsg.Visible = true;
                    lblMsg.Text = sqlex.ToString();
                }

                catch (Exception ex)
                {
                    lblMsg.Visible = true;
                    lblMsg.Text = ex.ToString();

                }

检查raiserror是否正常工作,我将插入中的表名更改为答案1的答案1,但不存在..

执行时我收到错误消息

System.Data.SqlClient.SqlException:无效的对象名称'answers1'。 EXECUTE之后的事务计数表示缺少COMMIT或ROLLBACK TRANSACTION语句。 System.Data.SqlClient.SqlConnection.OnError(SqlException异常,......

以前的count = 0,当前计数= 1

这件事情也正常,或者我错过了什么?

2 个答案:

答案 0 :(得分:2)

批处理(存储过程)在到达不存在的表(deferred name resolution)时正在中止,因此ROLLBACK没有执行。

来自MSDN/BOL

  

编译和声明级重新编译错误

     

有两种类型的错误   不会被TRY ... CATCH处理   错误发生在同一个执行中   等级为TRY ... CATCH构造:

     
      
  • 编译错误,例如阻止批处理的语法错误   执行。
  •   
  • 语句级重新编译期间发生的错误,例如   对象名称解析错误   由于编译后发生   延期名称解析。
  •   
     

批处理,存储过程或   包含TRY ... CATCH的触发器   构造生成其中之一   错误,TRY ... CATCH构造确实如此   不处理这些错误。

我建议你在顶部添加SET XACT_ABORT ON。这会强制ROLLBACK出现错误并“整理”。

还有一件事......

SET @q_id = IDENT_CURRENT('questions')

应该是

SET @q_id = SCOPE_IDENTITY()

编辑:

CREATE PROCEDURE ans_insert
    @q_desc varchar(2000),
    @sub_id int,
    @marks int,
    @ans1 varchar(1000),
    @ans varchar(1000),
    @userid varchar(15),
    @cr_date datetime
AS
BEGIN

SET NOCOUNT, XACT_ABORT ON; -- what I do

BEGIN TRY
....

答案 1 :(得分:0)

我认为它不会影响异常 - 但有些想法:

  • SCOPE_IDENTITY()会比IDENT_CURRENT更容易(也更可靠)吗? IDENT_CURRENT可以在并行操作期间从另一个会话返回ID。同时避免@@IDENTITY,这可能会受到INSERT s
  • 触发器的影响
  • 为什么不让调用(.NET)代码担心事务?在连接(SqlTransaction)或更宽(TransactionScope
  • 上更高级别管理更容易(也更通用)

示例SqlTransaction方法:

using(SqlTransaction tran = conn.BeginTransaction()) {
    try {
        // operations (may need to set command.Transaction = tran)
        tran.Commit();
    } catch {
        tran.Rollback();
        throw;
    }
}

示例TransactionScope方法(**必须SPAN连接**)

using(TransactionScope tran = new TransactionScope()) {
    // operations: note no other changes
    tran.Complete();
}