0

我需要从 SQL Server 存储过程的子句返回一个包含数据库错误的结果集,CATCH但我坚持下去。我是否需要使用游标来返回结果集,如果需要,那么OUTPUT我的 .NET 应用程序中参数的类型声明是什么?我试过了ObjectVariant但没有奏效。

我还尝试了仅使用SELECT语句返回的简单方法,它适用于一个存储过程,但不适用于另一个存储过程,如我的CATCH子句中所示:

 while (@I <= @count)
 begin      
    BEGIN TRY       
        -- delete all previous rows inserted in @customerRow for previous counts @I     
        delete from @customerRow

        -- this is inserting the current row that we want to save in database
        insert into @customerRow 
           SELECT  
               [id],[firstname], [lastname], [street], [city],
               [phone],[mobile],[fax], [email], [companyName],
               [licence],[brn], [vat], [companyStreet], [companyCity], [status]
           FROM 
               (SELECT  
                   ROW_NUMBER() OVER (ORDER BY id ASC) AS rownumber,
                   [id], [firstname], [lastname], [street], [city],
                   [phone], [mobile], [fax], [email], [companyName],
                   [licence], [brn], [vat], [companyStreet], [companyCity], [status]
                FROM    
                   @registerDetails) AS foo
          WHERE 
             rownumber = @I                                             

       -- this stored procedure handles the saving of the current customer row just defined above
        -- if there is any error from that sproc, it will jump to CATCH block 
        --save the error message in the temp table and continue 
        --with the next customer row in the while loop.
        exec dbo.sp_SaveCustomer @customerRow
    END TRY
    BEGIN CATCH
        IF @@TranCount = 0
        -- Transaction started in procedure.
        -- Roll back complete transaction.
        ROLLBACK TRANSACTION;
        if XACT_STATE()= -1 rollback transaction

        DECLARE @ErrorMessage NVARCHAR(4000);
        DECLARE @ErrorSeverity INT;
        DECLARE @ErrorState INT;


        SELECT @ErrorMessage = ERROR_MESSAGE() + ' ' + (select firstname from @registerDetails where id=@I)
        SELECT @ErrorSeverity = ERROR_SEVERITY();
        SELECT @ErrorState = ERROR_STATE() 

        INSERT INTO #registrationResults (error,id)
        SELECT @ErrorMessage, @I 

    END CATCH       

    set @I = @I +1              
end 
COMMIT TRANSACTION registerTran

select * from #registrationResults

当我在我的 vb.net 代码中调用它时,上述内容适用于一个存储过程:

ta.Fill(registrationErrors, clientDetailsDT)

whereregistrationErrorsclientDetailsDT是强类型数据表。

这个没有:

    begin catch
    IF @@TranCount > 0 or XACT_STATE()= -1 ROLLBACK TRANSACTION;
    DECLARE @ErrorMessage NVARCHAR(4000);
    DECLARE @ErrorSeverity INT;
    DECLARE @ErrorState INT;
    DECLARE @ErrorLine INT;


    SELECT @ErrorMessage = ERROR_MESSAGE();
    SELECT @ErrorSeverity = ERROR_SEVERITY();
    SELECT @ErrorState = ERROR_STATE();
    SELECT @ErrorLine = ERROR_Line();

    ****ERROR -- THIS SELECT WAS MESSING ALL UP as it was this select that was being        returned to the .NET and not the select of the desired #temp table after, hence returning 0    resultset as this select was EMPTY. !!  
    select status_indicator from InsertInvoiceTriggerData where session_GUID =   guid**
    delete from InsertInvoiceTriggerData where session_GUID = @guid**

    INSERT INTO #registrationResults (error,id)
    SELECT @ErrorMessage, NULL

    select * from #registrationResults

end catch

任何建议如何返回结果集?

4

1 回答 1

1

我还没有看到您的数据库代码,但根据我的经验,捕获的第一个错误catch意味着必须回滚整个事务。除了其他事情之外,这还意味着在任何给定情况下我永远不会返回超过 1 个错误。

因此,我在存储过程中使用了 2 个标量输出参数,即:

@Error int = null output,
@Message nvarchar(2048) = null output

我可以像任何其他输出变量一样检索它们。

UPD:即使您添加了一些代码,我仍然无法准确理解您的问题。但是,我发现您的代码存在一些问题,因此我会指出它们,并且很有可能其中一个可以解决问题。我只评论第一个片段,因为最后一个片段太不完整了。

  • 您应该在循环之前的某处开始最外层的事务。如果没有,代码将失败。
  • 如果我猜对了,您在dbo.sp_SaveCustomer存储过程中实现了所有保存点逻辑。如果不是,那么整个讨论就毫无意义,因为您显示的代码中没有save tranorrollback @savepoint语句。
  • 第一个catch陈述 -IF @@TranCount = 0 ROLLBACK TRANSACTION都是错误的。如果条件成功,将导致尝试回滚不存在的事务时出错。如果您依赖保存点,则不应该在这里。
  • 接下来应该导致无条件中断: if XACT_STATE()= -1 begin rollback transaction; break; end;
  • 其余的 catch 代码可以替换为: INSERT INTO @registrationResults (error, id) SELECT error_message() + ' ' + firstname, id from @registerDetails where id=@I;
  • 此外,永远不要为此目的使用临时表,因为回滚也会影响它们。为此始终使用表变量,它们是非事务性的(就像任何其他变量一样)。
  • 应该是有条件的,因为此时commit您可能最终没有要提交的事务: if @@trancount > 0 commit tran;
  • commit语句中指定保存点名称没有意义,它只会导致混淆(尽管不被视为错误)。此外,此模块中不应有任何保存点(除非您在循环之前定义了它)。

我怀疑这只是冰山一角,因为我不知道dbo.SaveCustomer存储过程中实际发生了什么。

UPD2:这是我用来从存储过程接收记录集的 VB.NET 代码示例:

Private Function SearchObjectsBase( _
    SearchMode As penum_SEARCH_MODE, SearchCriteria As String
) As DataSet

Dim Cmd As DbCommand, Pr As DbParameter, dda As DbDataAdapter

' Initialise returning dataset object
SearchObjectsBase = New DataSet()

Cmd = MyBase.CreateCommand(String.Format("dbo.{0}", SearchMode))

With Cmd

    ' Parameter definitions
    Pr = .CreateParameter()
    With Pr
        .ParameterName = "@SearchCriteria"
        .DbType = DbType.Xml
        .Value = SearchCriteria
    End With
    .Parameters.Add(Pr)

    ' Create data adapter to use its Fill() method
    dda = DbProviderFactories.GetFactory(.Connection).CreateDataAdapter()
    ' Assign the prepared DbCommand as a select method for the adapter
    dda.SelectCommand = Cmd

    ' A single resultset is expected here
    dda.Fill(SearchObjectsBase)

End With

' Set error vars and get rid of it
Call MyBase.SetErrorOutput(Cmd)

' Check for errors and, if any, discard the dataset
If MyBase.ErrorNumber <> 0 Then SearchObjectsBase.Clear()

End Function

我使用.NET 4.5,它有一个非常好的方法,可以根据实际连接自动选择最合适的数据适配器。这是此函数的调用:

Dim XDoc As New XElement("Criteria"), DS As DataSet = Nothing, DT As DataTable
...
DS = .SearchPatients(XDoc.ToString(SaveOptions.None))
' Assign datasource to a grid
Me.dgr_Search.DataSource = DS.Tables.Item(0)

在这里,SearchPatients() 是 SearchObjectsBase() 之上的一个包装器。

于 2014-11-28T00:33:33.127 回答