[英]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.