简体   繁体   English

存储过程不更新记录

[英]Stored procedure not updating record

I have an issue with a stored procedure where it is updating one table, but not another, and it only happens on the odd occasion, not all the time.我有一个存储过程的问题,它正在更新一个表,而不是另一个表,它只发生在奇怪的情况下,而不是一直发生。 This is the stored procedure:这是存储过程:

ALTER PROCEDURE [dbo].[AddTransaction]
    @BeneficiaryName VARCHAR(200),
    @Amount DECIMAL(18,2),
    @Reference VARCHAR(200),
    @Direction INT,
    @Currency INT,
    @CurrencyCulture VARCHAR(5),
    @Status INT,
    @Month INT,
    @Year INT,
    @BankAccountId INT,
    @BusinessId INT,
    @FeeType INT,
    @IncomingPaymentsPercentage DECIMAL(18,2),
    @IncomingPaymentFee DECIMAL(18,2),
    @CompletedPaymentFee DECIMAL(18,2),
    @DateProcessed DATETIME
AS
BEGIN
    
    DECLARE @oldBalance DECIMAL(18,2)
    DECLARE @newBalance DECIMAL(18,2)
    DECLARE @fee DECIMAL(18,2)
    DECLARE @amountAfterFee DECIMAL(18,2)

    BEGIN TRANSACTION
        -- SET NOCOUNT ON;
        
        -- lock table 'BankAccounts' till end of transaction
        SELECT @oldBalance = availableBalance
        FROM BankAccounts
        WITH (TABLOCKX, HOLDLOCK)
        WHERE Id = @BankAccountId

        -- set the new balance
        SET @newBalance = @oldBalance + @Amount

        UPDATE BankAccounts SET AvailableBalance = @newBalance WHERE Id = @BankAccountId

        -- insert a new transaction
        IF @FeeType = 1
        BEGIN
            IF @Direction = 1
            BEGIN
                SET @fee = @Amount * (@IncomingPaymentsPercentage / 100)
                SET @amountAfterFee = @Amount - @fee;
            END;
            ELSE
            BEGIN
                SET @fee = 0.00
                SET @amountAfterFee = @Amount
            END;
        END;
        ELSE
        BEGIN
            IF @Direction = 1
            BEGIN
                SET @amountAfterFee = @Amount - @IncomingPaymentFee
                SET @fee = @IncomingPaymentFee
            END;
            ELSE
            BEGIN
                SET @amountAfterFee = @Amount - @CompletedPaymentFee
                SET @fee = @CompletedPaymentFee
            END;
        END;

        INSERT INTO Transactions
            (BeneficiaryName, amount, Reference, Direction, Currency, CurrencyCulture, DateAdded, [Status], DateProcessed, [Month], [Year], Balance, BankAccountId, AmountAfterFee, Fee)
        VALUES
            (@BeneficiaryName, @Amount, @Reference, @Direction, @Currency, @CurrencyCulture, @DateProcessed, @Status, @DateProcessed, @Month, @Year, @newBalance, @BankAccountId, @amountAfterFee, @fee)
    
    -- now commit the transaction
    COMMIT

The insert in to the transactions table is working all the time, but on the odd occasion, the update on the BankAccounts table doesn't happen.对交易表的插入一直在工作,但在奇怪的情况下,BankAccounts 表上的更新不会发生。 This line here:这条线在这里:

UPDATE BankAccounts SET AvailableBalance = @newBalance WHERE Id = @BankAccountId

I am not sure if this is anything to do with the lock, but I thought you could still update the locked table within the same transaction.我不确定这是否与锁定有关,但我认为您仍然可以在同一事务中更新锁定的表。 For more info, there could be multiple calls to this stored procedure in the same second, probably no more than 10, but often the issue is when nothing else is accessing the BankAccount record it is updating.有关更多信息,可能在同一秒内多次调用此存储过程,可能不超过 10 次,但通常问题是当没有其他人访问正在更新的 BankAccount 记录时。

First off, TABLOCKX on a SELECT statement does not take an X-lock.首先, SELECT语句上的TABLOCKX不带 X 锁。 SELECT statements never take X-locks anyway. SELECT语句从不使用 X 锁。 Furthermore, it is unnecessary and extremely inefficient to lock the entire table.此外,锁定整个表是不必要的,而且效率极低。

Secondly, even HOLDLOCK is not enough here, because if no data has been modified, the lock will not be taken.其次,这里即使是HOLDLOCK也是不够的,因为如果没有数据被修改过,锁是不会被拿走的。 You must use UPDLOCK for this to work as expected.必须使用UPDLOCK才能按预期工作。

To be honest, you don't actually need the SELECT anyway, as the whole thing can be done in one atomic statement:老实说,您实际上并不需要SELECT ,因为整个事情都可以在一个原子语句中完成:

I note that @oldBalance isn't actually used.我注意到@oldBalance并未实际使用。

UPDATE BankAccounts WITH (HOLDLOCK)
SET AvailableBalance +=  @amount,
    @newBalance = AvailableBalance + @amount
WHERE Id = @BankAccountId;

You can even combine the Transaction insert by using an OUTPUT clause, this enables you to entirely remove the BEGIN TRANSACTION/COMMIT您甚至可以使用OUTPUT子句组合Transaction插入,这使您能够完全删除BEGIN TRANSACTION/COMMIT

ALTER PROCEDURE [dbo].[AddTransaction]
    @BeneficiaryName VARCHAR(200),
    @Amount DECIMAL(18,2),
    @Reference VARCHAR(200),
    @Direction INT,
    @Currency INT,
    @CurrencyCulture VARCHAR(5),
    @Status INT,
    @Month INT,
    @Year INT,
    @BankAccountId INT,
    @BusinessId INT,
    @FeeType INT,
    @IncomingPaymentsPercentage DECIMAL(18,2),
    @IncomingPaymentFee DECIMAL(18,2),
    @CompletedPaymentFee DECIMAL(18,2),
    @DateProcessed DATETIME
AS

SET NOCOUNT ON;
    
DECLARE @fee DECIMAL(18,2);
DECLARE @amountAfterFee DECIMAL(18,2);

SET @fee = CASE WHEN @FeeType = 1 THEN
    CASE WHEN @Direction = 1
        THEN @Amount * (@IncomingPaymentsPercentage / 100)
        ELSE 0.0 END
    ELSE
    CASE WHEN @Direction = 1
        THEN @IncomingPaymentFee
        ELSE @CompletedPaymentFee END
    END;
SET @amountAfterFee = @Amount - @fee;

UPDATE BankAccounts WITH (HOLDLOCK)
SET AvailableBalance += @amount

OUTPUT
    @BeneficiaryName, @Amount, @Reference, @Direction, @Currency,
    @CurrencyCulture, @DateProcessed, @Status, @DateProcessed,
    @Month, @Year, inserted.AvailableBalance, @BankAccountId, @amountAfterFee, @fee

INTO Transactions
   (BeneficiaryName, amount, Reference, Direction, Currency,
    CurrencyCulture, DateAdded, [Status], DateProcessed, [Month], [Year],
    Balance, BankAccountId, AmountAfterFee, Fee)

WHERE Id = @BankAccountId;

GO

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM