如何在同时调用多次的存储过程中读取更新的数据

How to read updated data in a stored procedure called multiple times simultaneously

有2个table:

有一个存储过程处理(我想用 ACID 操作):

当有多个同时调用SP时出现问题,实际上PreviousBalance的值不正确(顺序错误),导致SP中读取旧值,同时另一个调用过程是运行.

要更好地理解,请查看以下屏幕截图。

有 3 个具有相同 DT 的交易(ID 1289、1288、1287),在所有这些交易中,PreviouseBalance 是相等的,但不正确,因为 :

的值

我认为问题出在@OLDBalance var 的 SET 中;同时这 3 个线程读取相同的值,因此当 SP 转到 INSERT 时加载相同的 PreviousBalance 值。

我该怎么做才能在提交一个操作后正确读取@OLDBalance? 我尝试在SP中设置了几种类型的Isolation Levet,结果都是一样的,有时会出错死锁。

我有以下存储过程:

存储过程

ALTER PROCEDURE [dbo].[upsMovimenta_internal]
    @AccountID int, 
    @Amount money, 
    @TypeTransactionID int, 
    @ProductID int, 
    @notes nvarchar(max)
AS
BEGIN
    SET NOCOUNT ON;

    DECLARE @OLDBalance MONEY;
    DECLARE @PreviousBalance MONEY;
    DECLARE @CurrentBalance MONEY;

    DECLARE @Molt float;
    BEGIN TRANSACTION;

    IF NOT EXISTS( SELECT * FROM Accounts WHERE AccountID = @AccountID) 
    BEGIN
        RaisError(N'Account not found ', 10, 1, N'number', 3);
        return (1)
    END

    SELECT @Molt = Moltiplicatore 
        FROM TypeTransactions 
            where TypeTransactionID = @TypeTransactionID
    ;

    IF (@Molt is null )
    BEGIN
        RaisError(N'Error transaction', 10, 1, N'number', 3);
        return (1)
    END

    SET @Amount = @Amount * @Molt;

    --SELECT * FROM Wallets
    SELECT TOP 1 @OLDBalance = TotalAmount 
        FROM Wallets 
        where AccountID = @AccountID
        ;

        SET @CurrentBalance = @OLDBalance + @Amount;
        
        IF (@ProductID = 1 )
        BEGIN
            UPDATE Wallets
                    SET TotalAmount+=@Amount, 
                        Cash+=@Amount
                FROM Wallets where AccountID = @AccountID
                ;
        END

        IF (@ProductID = 2 )
        BEGIN
            UPDATE Wallets
                    SET TotalAmount+=@Amount, 
                        Fun+=@Amount
                FROM Wallets where AccountID = @AccountID
                ;
        END

        INSERT INTO Transactions  
             ( AccountID, ProductID, DT, TypeTransactionID, Amout, Balance, PreviousBalance, Notes )  
             VALUES
             ( @AccountID, @ProductID, GETDATE(), @TypeTransactionID, @Amount, @CurrentBalance, @OLDBalance, @notes)
        ; 

    COMMIT TRANSACTION;

    return (0)

END

非常感谢你们

通常,管理记录锁的一种方法是在开始事务后立即对要处理的行应用虚拟更新。

在这种情况下SQL 服务器保证这些行将被锁定并且没有其他事务可以访问这些行。所以你可以把你的设计改成这样:

begin tran

update myTable 
set Field1 = Field1
where someKeyField = 212

-- do the same for other tables that you want to protect against change

-- at this moment all working rows will be locked, other procedure calls will be on hold

-- do your main operations here

commit tran

问题是其他进程调用将等待,这可能会降低性能,甚至 time-out 如果流量很高并且您在此进程中的操作很长

如果您在高事务环境中工作,则需要更改您的设计。

更新:设计建议

我不明白为什么你的交易中有 PreviousBalanceBalance(这违反了设计规则,但是在特殊情况下你可以覆盖规则)。

您可能需要它来加快计算速度或简化查询。但这在 OLTP 数据库中不是好的做法。 规则说您保留 Amount 列并在其他地方计算 PreviousBalanceBalance

您应该删除 PreviousBalance 但保留 Balance 列,并且每次插入交易时,您都会更新 (increase/decrease) Balance 列。 此外,您需要在第一次交易时初始化 Balance 列。

这是我能想到的。如果我了解你的整个系统,我将能够有更好的想法。