【问题标题】:SQL Server stored procedure throws multiple errorsSQL Server 存储过程引发多个错误
【发布时间】:2018-03-20 12:17:37
【问题描述】:

我有一个存储过程,我执行INSERT,然后执行RAISERROR("MyException", 5, 5),在这种情况下插入失败。

问题是我的 .NET 应用程序的结果是

MyException: 无法插入值 NULL...

所以它会合二为一地返回 2 个异常。

我的 .NET 代码总是将整个字符串与“MyException”进行匹配,但不再起作用了。

这是标准吗?如果是这样,这以前怎么可能有效?有什么设置吗?

编辑:

我正在使用 .NET 表适配器来处理 SQL 数据库。

版本

产品 : Microsoft SQL Server Enterprice(64-bit) 版本:11.0.2100.60 集群:假 HADR:错误

微软框架 .NET 4.6.2

存储过程

ALTER Procedure [dbo].[up_kod_text_save]
            -- Add the parameters for the stored procedure here
            @entity_id int,
    @text varchar(300),
            @kod_key int,

            @kod_id int,
    @korttext varchar(10),
    @inaktiv bit,
    @primar_extern_kod varchar(300),
    @sparad_av varchar(128),
    @kod_typ_id int,
    @kod varchar(20),
    @original_rowver rowversion,
    @associerat_varde decimal(18,5),
    @beskrivning varchar(2000),
    @viktigt_varde bit
AS

BEGIN
            -- SET NOCOUNT ON added to prevent extra result sets from
            -- interfering with SELECT statements.
            SET NOCOUNT ON;
    DECLARE @resultat table(kod_id int,  uppdat_tidpunkt datetime, rowver binary(8) );

            Insert into @resultat 
                         exec up_kod_save @kod_id,@text,@korttext,@inaktiv,@primar_extern_kod,@sparad_av,@kod_typ_id,@kod,@original_rowver, @associerat_varde, @beskrivning,@viktigt_varde

            declare @uppdat_tidpunkt datetime 
            declare @rowver rowversion

            declare @tablename varchar(30)
            declare @idname varchar(30)

            SET @rowver = (SELECT rowver FROM @resultat)
            SET @kod_id = (SELECT kod_id from @resultat)

------------------------------------------------------------------------------------
            set @uppdat_tidpunkt = getdate()

            IF(@kod_key = 2)
            BEGIN
            ELSE IF(@kod_key = 11)
            BEGIN
                         IF  EXISTS ( SELECT akut_checklista_id FROM akut_checklista WHERE akut_checklista_id = @entity_id )
                         BEGIN
                                     UPDATE akut_checklista
                                     SET [text] = @text, 
                                     [kod_id] = @kod_id
                                     WHERE akut_checklista_id = @entity_id
                         END       
                         ELSE
                         BEGIN   
                                     -- Skapa master-rad
                                     INSERT INTO [akut_checklista] ([text], [kod_id])
                                     VALUES (@text, @kod_id);
                                     set @entity_id = SCOPE_IDENTITY()
                         END
            END
            ELSE
            BEGIN
                         RAISERROR ('MyApp_EXCEPTION_UPPDATERAD_AV_ANNAN',16,1)
                         RETURN
            END


            SELECT @entity_id as entity_id, @rowver as rowver, @kod_id as kod_id, @uppdat_tidpunkt as uppdat_tidpunkt

up_kod_save 只是更新\插入,没有任何事务。但是,如果 rowversion 错误,它可能会失败。

【问题讨论】:

  • 能否请您提供有关 sql-server 版本和 .net 版本的信息?结构os存储过程呢?交易?尝试/捕捉
  • 我的 .NET 代码总是将整个字符串与“MyException”匹配,但是...你能分享一下执行此操作的代码吗?
  • 指定的严重性必须为 11 或更高,RAISERROR 才能引发异常。试试RAISERROR('MyException',16,5) 。请注意,单引号允许您使用 QUOTED_IDENTIFIER ON 运行,这是一种最佳做法。
  • 我现在更新了一些信息。 @destination-data,.NET 代码不是问题。我可以看到 sp 的结果是 2 条错误消息合二为一。在这种情况下,首先是 akut_checklista 中关于 null 的异常,然后是因为 Uppdaterad_Av....
  • 您是否尝试过使用分析器并获取您的应用程序正在尝试执行的操作并将查询放在 SQL 上?首先,我会这样做来检查.. 在这里粘贴你的分析器插入也

标签: .net sql-server exception stored-procedures


【解决方案1】:

SqlClient API 引发异常时,批处理执行期间发生的严重性为 11 或更高的错误将作为 SqlException 返回到应用程序。各个错误在 SqlException.Errors 集合中返回,SqlException.Message 包含集合中各个错误的连接文本。

一些 SQL Server 错误会终止批处理,因此在错误之后不会执行批处理中的后续语句。在这种情况下,仅返回批处理终止错误之前发生的错误。当发生多个错误并且没有一个是批处理终止时,将返回所有错误。所以,根据具体的错误,后面的RAISERROR可能不会被执行。

请记住,默认行为是 SQL Server 将继续在您的内部和外部过程中执行 T-SQL 语句,不会发生批处理终止错误,包括严重性为 16 的RAISERROR。这可能导致多个返回错误。

通常不希望在出错后继续批处理执行并引发单个错误。这可以通过以下一种或多种技术来避免:

  • 使用结构化错误处理 (TRY/CATCH) 并使用 THROW 而不是 RAISERROR

  • 在每个语句之后检查@@ERROR 以及控制流语句

  • 指定 SET XACT_ABORT_ON 以便将非批处理终止错误提升为批处理终止错误并回滚事务

SET XACT_ABORT_ON 是具有显式事务的存储过程中的最佳实践,以确保在客户端超时或查询取消的情况下回滚事务。除非 TRY/CATCH 块在范围内,否则批处理也将终止。

在您的情况下,我建议您将 TRY/CATCH 块添加到外部 proc。 CATCH 块将在内部或外部过程中出现错误后执行。然后,CATCH 块代码可以使用THROW(SQL Server 2012 及更高版本)引发原始错误,或者使用RAISERROR 严重性 11+ 的用户定义错误引发客户端应用程序中的异常。下面是一个例子。

CREATE PROCEDURE [dbo].[up_kod_text_save]
    -- parameters here
AS
SET NOCOUNT ON;
BEGIN TRY
    -- code here
END TRY
BEGIN CATCH --catch block will be entered after an error in either proc
    IF @@TRANCOUNT > 0 ROLLBACK; --needed only if BEGIN TRAN is used
    THROW; --this will raise the original error
END;
GO

【讨论】:

  • 更多详情请参见MS Docs RAISERROR的备注部分。另请注意,从同一链接的介绍中可以看出,“新应用程序应改为使用 THROW。”
  • up_kod_save 被执行,这个 SP 中的逻辑将导致 RAISERROR ('MyApp_EXCEPTION_UPPDATERAD_AV_ANNAN',16,1)。奇怪的是,这只会让你离开 up_kod_save 而不是 up_kod_text_save。 up_kod_text_save 将继续执行,就好像没有抛出错误一样。因为结果为空,所以 kod_id 也将为空,并且在插入时会导致新的 SQL 异常。此时我们将被排除在 up_kod_text_save 之外,但有不止一个异常?
  • 这之前一直有效(仅返回一个异常),所以我们不确定为什么这种行为发生了变化。也许有一些更新?
  • @Banshee,我在答案中添加了更多信息。我不知道对 SqlClient 行为的更改。我对 .NET 2.0 及更高版本进行了一些粗略的测试,但没有发现差异。
猜你喜欢
  • 2015-12-10
  • 2016-02-15
  • 1970-01-01
  • 2015-06-14
  • 1970-01-01
  • 2018-10-04
  • 2012-05-25
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多