【问题标题】:sql transaction does not log when failed but the transaction still goes throughsql事务失败时不记录,但事务仍然通过
【发布时间】:2018-01-09 01:30:17
【问题描述】:

当交易的所有要求都得到满足时,我的程序会正确注册所有内容并记录交易信息。

但是当给出错误信息时,信息不会被记录,但交易仍然会进行,我不确定我做错了什么。

首先是记录事务信息的存储过程

/****** Object:  StoredProcedure [dbo].[LogTransactionAttempt]    Script Date: 8/1/2017 9:57:24 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

ALTER PROCEDURE [dbo].[LogTransactionAttempt]
    (@ToUserAccountId int,
     @FromUserAccountId int,
     @TransferAmount money,
     @CreditorWithdrawl nvarchar(50),
     @TransferCode numeric(18,0))
AS
BEGIN
    INSERT INTO Transactions (FromUserAccountId, ToUserAccountId, TransferAmount, 
                              CreditorWithdrawal, TransferCode, TransactionDateTime)
    VALUES (@FromUserAccountId, @ToUserAccountId, @TransferAmount, 
            @CreditorWithdrawl, @TransferCode, GETDATE())

    RETURN @@IDENTITY
END 

这是整个过程

/****** Object:  StoredProcedure [dbo].[performTransaction]    Script Date: 8/1/2017 9:57:05 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

ALTER PROCEDURE [dbo].[performTransaction]
    (@ToUserAccountId int,
     @FromUserAccountId int,
     @TransferAmount money,
     @CreditorWithdrawl nvarchar(50),
     @TransferCode numeric(18,0))
AS
BEGIN
    DECLARE @FromUserAccountTest INT

    SELECT @FromUserAccountTest = (SELECT Count(*) 
                                   FROM dbo.UserAccount
                                   WHERE UserAccountId = @FromUserAccountID)

    IF @FromUserAccountTest <> 1 
    BEGIN
        RAISERROR('From Account Does Not Exist', 10, 1)
    END

    DECLARE @ToUserAccountTest INT

    SELECT @ToUserAccountTest = (SELECT Count(*) 
                                 FROM dbo.UserAccount   
                                 WHERE UserAccountId = @ToUserAccountID)

    IF @ToUserAccountTest <> 1 
    BEGIN
        RAISERROR('To Account Does Not Exist', 10, 2)
    END

    -- At this point we can log the transaction
    DECLARE @TransactionID INT 

    EXEC @TransactionID = dbo.LogTransactionAttempt  @ToUserAccountId, @FromUserAccountId, @TransferAmount, @CreditorWithdrawl, @TransferCode

    -- Check to see if sufficient balance
    DECLARE @FromAccountBalance  MONEY

    SELECT @FromAccountBalance = (SELECT CreditBalance
                                  FROM dbo.UserAccount
                                  WHERE UserAccountID = @FromUserAccountID)

    IF (@FromAccountBalance < @TransferAmount)     
    BEGIN
        RAISERROR('Insufficient Balance', 10, 3)
    END

    -- Now we can start the transaction
    BEGIN TRANSACTION 
        UPDATE UserAccount
        SET CreditBalance = CreditBalance - @TransferAmount
        WHERE UserAccountId = @FromUserAccountID

        IF (@@ERROR <> 0)
        BEGIN
            ROLLBACK TRANSACTION   /* if errors - rollback transaction */
            RETURN 
        END

        UPDATE UserAccount
        SET CreditBalance = CreditBalance + @TransferAmount
        WHERE UserAccountID = @ToUserAccountID

        IF (@@ERROR <> 0)
        BEGIN
            ROLLBACK TRANSACTION   /*if errors - rollback transaction */
            RETURN 
        END

        UPDATE dbo.Transactions
        SET Successful = 1
        WHERE TransactionID = @TransactionID 

        IF (@@ERROR <> 0)
        BEGIN
            ROLLBACK TRANSACTION   /* if errors - rollback transaction */
            RETURN 
        END

        -- Transaction
        COMMIT TRANSACTION

END -- Procedure 

就像我说的,当输入正确时一切正常,但如果输入错误,例如帐户不存在或资金不足,它不会记录失败,但交易仍会进行

编辑 尝试从 10 切换到 16 无效

将 100 美元从余额为 100 美元的帐户 11 转移到没有余额的帐户 22 的正确交易示例

USE [Transaction]
GO

DECLARE @return_value int

EXEC    @return_value = [dbo].[performTransaction]
    @ToUserAccountId = 22,
    @FromUserAccountId = 11,
    @TransferAmount = 100,
    @CreditorWithdrawl = N'credit',
    @TransferCode = 1

SELECT  'Return Value' = @return_value

GO

正确的条目返回

1017    22  11  100.0000    credit  1   2017-08-02 10:19:23.110 yes

在事务日志中

输入错误信息,例如从余额仅为 100 美元的账户中转出 150 美元,会正确地引发错误代码,但不会取消交易,即使请求的钱并不全部存在

USE [Transaction]
GO

DECLARE @return_value int

EXEC    @return_value = [dbo].[performTransaction]
    @ToUserAccountId = 22,
    @FromUserAccountId = 11,
    @TransferAmount = 150,
    @CreditorWithdrawl = N'credit',
    @TransferCode = 2

SELECT  'Return Value' = @return_value

GO

在消息中返回以下内容

(1 row(s) affected)
Msg 50000, Level 16, State 3, Procedure performTransaction, Line 72 [Batch Start Line 2]
Insufficient Balance

(1 row(s) affected)

(1 row(s) affected)

(1 row(s) affected)

(1 row(s) affected)

所以看到了错误但没有记录错误的交易,但交易仍然通过导致负余额

USE [Transaction]
GO
/****** Object:  StoredProcedure [dbo].[LogTransactionAttempt]    Script Date: 8/2/2017 9:20:54 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO


ALTER PROCEDURE [dbo].[LogTransactionAttempt]


(


  @ToUserAccountId int,
  @FromUserAccountId int,
  @TransferAmount money,
  @TransferCode numeric(18,0)
)

 AS

 BEGIN

  INSERT INTO Transactions

  (

    UserAccountId,
    TransferAmount, 
    CreditorWithdrawal,
    TransferCode,
    TransactionDateTime
  )

  VALUES 

  (
    @FromUserAccountId,
    @TransferAmount, 
    'Withdrawl',
    @TransferCode,
    GETDATE()
  )
  ,
  (
    @ToUserAccountId,
    @TransferAmount, 
    'Credit',
    @TransferCode,
    GETDATE()
  )



  RETURN @@IDENTITY



  END 

【问题讨论】:

  • 错误级别为 10 的 Raiserror 可能太低而无法正确捕获。我会尝试将它提高到 16。如果您没有使用 try/catch 块,您可能还需要将 xact_abort 设置为 on。这至少应该防止后续交易在您遇到 raiserror 时继续并提交

标签: sql-server transactions


【解决方案1】:

Raiserror 不会设置@@Error,除非您的严重级别为 16 或更高。少于 16 条的消息被认为是信息性的,而不是错误。试试下面的代码验证一下:

RAISERROR('Insufficient Balance', 10, 3)
select @@Error; -- will be 0
RAISERROR('Insufficient Balance', 16, 3)
select @@Error; -- will be 50000

另外,请注意 RAISERROR 永远不会中断执行,除非您执行诸如引发导致连接立即结束的致命错误之类的操作(您需要系统管理员权限)。因此,除了您使用显式“返回”的位置外,预计 RAISERROR 之后的所有代码都会执行。

阅读本文了解更多关于 RAISERROR 和中断的信息:https://social.msdn.microsoft.com/Forums/sqlserver/en-US/483d6be7-6518-41c9-8138-aa1412ae8252/raiseerror-to-stop-execution?forum=transactsql

另外你还需要

DECLARE @errcode INT 

after 

DECLARE @FromUserAccountTest INT;

添加

select @errcode = @@ERROR 

immediately after each raiserror  

原因是任何成功的事件都会将 @@ERROR 重置为 0 - 因此以下所有操作都会将您的 @@ERROR 变量重置为 0;

SELECT @FromUserAccountTest = (SELECT Count(*) 
                               FROM dbo.UserAccount
                               WHERE UserAccountId = @FromUserAccountID)

SELECT @FromUserAccountTest = (SELECT Count(*) 
                               FROM dbo.UserAccount
                               WHERE UserAccountId = @FromUserAccountID)


EXEC @TransactionID = dbo.LogTransactionAttempt  @ToUserAccountId, @FromUserAccountId, @TransferAmount, @CreditorWithdrawl, @TransferCode

    UPDATE UserAccount
    SET CreditBalance = CreditBalance - @TransferAmount
    WHERE UserAccountId = @FromUserAccountID

然后最后改变你的第一次出现

IF @@ERROR <> 0

IF @@errcode <> 0

将第二个匹配项保留为

IF @@ERROR <> 0

因为它正在测试之前更新语句的结果。

【讨论】:

  • 当给出错误信息时,错误确实会捕获,错误消息会正确弹出,它表示交易继续,我遇到了问题,并且错误的交易没有正确记录,而正确的交易确实.如何让它在回滚任何错误事务的同时记录成功和不成功的事务?我会尝试像你提到的那样从 10 切换到 16。编码不是我的强项之一,所以如果我遗漏了一些非常容易解决的问题,我们深表歉意。
  • “不记录失败”是什么意思?您是指调用您的 [LogTransactionAttempt] 吗?您的意思是它根本没有写入您的事务表,还是表明它成功了?如果是第一个,您能否提供一个包含一些示例数据和表定义的最小工作示例供我复制:-)?如果是第二个,更改严重性级别应该可以解决问题。此外,代码缩进在未来也很有帮助:-)。
  • 当事务完成且没有任何错误时,它会在表中记录有关事务的信息,该部分工作正常。当查询中引入错误信息时,例如不存在的帐户,事务完成但不记录,而不是回滚事务并将失败的事务信息记录在事务表中。
  • 至于代码缩进,一切都在 sql server management studio 软件中正确设置,但由于某种原因它没有为我正确复制。对此感到抱歉。
  • 因此,据我所知,该问题不是由您上面包含的任何代码引起的。严重级别为 10 时会发生这种情况吗?如果是这样,您的交易表是否有任何账户外键,或者是否有其他原因导致您的插入交易语句出错?另一个问题可能是您使用严重级别 16 并且您正在从事务中调用 [dbo].[performTransaction]。如果不是这些,我必须尝试复制才能看到。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-01-02
  • 1970-01-01
  • 2019-06-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多