sql事务在失败时不记录,但事务仍然通过

时间:2017-08-02 02:13:03

标签: sql-server transactions

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

但是当提供错误信息时,信息不会被记录,但交易仍然存在,我不确定我做错了什么。

首先是记录交易信息的存储过程

/****** 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无效

正确交易的示例,从账户11移动100美元,余额为100美元,账户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 

1 个答案:

答案 0 :(得分:1)

除非您的严重性级别为16或更高,否则Raiserror不会设置@@ Error。小于16的消息被视为信息性而非错误。请尝试以下代码进行验证:

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

另外,请注意RAISERROR永远不会中断执行,除非你做了类似引发致命错误的事情,导致连接立即结束(你需要sysadmin权限)。因此,除了使用显式“返回”的地方之外,期望执行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

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