在我的系统中,我想创建与银行帐户非常相似的功能。我有一些可以操作的余额。让我们通过一个例子来说明它。
我有两个用于余额和操作的表:
Balances
(
Id int,
Balance decimal
)
Operations
(
Id int,
FK_BalancesId int,
Value decimal
)
当我想将500添加到特定余额时,我只需要保存带有BalanceId和要添加的值的操作。同样,我要减去500,我只保存值“ -500”的操作。当然,在保存减法运算之前,我必须检查添加到余额上的所有操作中的余额是否高于减法值。
我的问题是,当两个人在差不多相同的时间想减去“ -500”时。 当某人想添加运算“ -500”时,我只是获得所有特定余额的运算,将它们与余额相加,然后检查总和是否大于或等于500。(我不想在余额上获得负数) 。但是,当两个人想要在差不多相似的时间进行这样的手术时的情况呢?这是我不知道如何处理的情况:
A wants add operation "-500"
B wants add operation "-500"
System check for A - balance is 750 operation is allowed
System check for B - balance is 750 operation is allowed
A save operation "-500"
B save operation "-500"
然后该余额低于0(-250)。如何正确清洁这种情况?
答案 0 :(得分:0)
假设整个事情都应该在SQL Server中完成,并且这些是下面的真实表定义-我将Operations表中的“ Id”更改为Identity列。以下过程将检查余额,以确保交易不会产生负余额,用给定的金额更新余额,并返回插入的操作ID的值。如果余额将变为负数,则将返回NULL作为操作ID,并且余额保持不变。由于您在问题和示例中的值两边加上了引号,因此该过程允许您以字符串形式传递操作。
CREATE TABLE Balances
(
Id INT,
Balance DECIMAL
)
CREATE TABLE Operations
(
Id INT IDENTITY(1,1) NOT NULL,
FK_BalancesId INT,
Value DECIMAL
)
GO
--insert the beginning balance
insert into Balances values (1, 750)
GO
CREATE PROCEDURE sp_SaveOperation
@BalanceId INT
, @OperationAmount VARCHAR(100)
AS
DECLARE @OperationsId INT
DECLARE @Balance DECIMAL
DECLARE @Amount DECIMAL
SET @Amount = CAST(@OperationAmount AS DECIMAL)
BEGIN TRANSACTION
SET @Balance = (SELECT B.Balance FROM Balances WITH (SERIALIZABLE) WHERE Id = @BalanceId)
-- [+] operation assuming amount will be a negative number
IF (@Balance + @Amount > 0)
BEGIN
UPDATE Balances SET Balance = Balance + @Amount
INSERT INTO Operations (FK_BalancesId, Value) VALUES (@BalanceId, @Amount)
SET @OperationsId = SCOPE_IDENTITY()
END
COMMIT TRANSACTION
SELECT @OperationsId
用法:
exec sp_SaveOperation 1, '-500';
_________________________________
Returns 1
exec sp_SaveOperation 1, '-500';
_________________________________
Returns NULL
select * from balances;
____________________________
Id Balance
1 250
select * from operations;
____________________________
Id FK_BalancesId Value
1 1 -500