存储过程不更新记录
Stored procedure not updating record
我有一个存储过程的问题,它正在更新一个 table,而不是另一个,而且它只是偶尔发生,而不是一直发生。这是存储过程:
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
插入交易 table 一直有效,但偶尔,BankAccounts table 的更新不会发生。这行在这里:
UPDATE BankAccounts SET AvailableBalance = @newBalance WHERE Id = @BankAccountId
我不确定这是否与锁定有关,但我认为您仍然可以在同一事务中更新锁定的 table。有关详细信息,同一秒内可能会多次调用此存储过程,可能不超过 10 次,但通常问题是没有其他任何东西正在访问它正在更新的 BankAccount 记录。
首先,SELECT
语句中的 TABLOCKX
不使用 X 锁。 SELECT
语句永远不会使用 X 锁。此外,锁定整个 table.
是不必要的,而且效率极低。
其次,这里即使HOLDLOCK
也不够,因为如果没有修改数据,就不会拿锁。 您必须使用UPDLOCK
才能按预期工作。
老实说,您实际上并不需要 SELECT
,因为整个事情可以在一个原子语句中完成:
I note that @oldBalance
isn't actually used.
UPDATE BankAccounts WITH (HOLDLOCK)
SET AvailableBalance += @amount,
@newBalance = AvailableBalance + @amount
WHERE Id = @BankAccountId;
您甚至可以使用 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
我有一个存储过程的问题,它正在更新一个 table,而不是另一个,而且它只是偶尔发生,而不是一直发生。这是存储过程:
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
插入交易 table 一直有效,但偶尔,BankAccounts table 的更新不会发生。这行在这里:
UPDATE BankAccounts SET AvailableBalance = @newBalance WHERE Id = @BankAccountId
我不确定这是否与锁定有关,但我认为您仍然可以在同一事务中更新锁定的 table。有关详细信息,同一秒内可能会多次调用此存储过程,可能不超过 10 次,但通常问题是没有其他任何东西正在访问它正在更新的 BankAccount 记录。
首先,SELECT
语句中的 TABLOCKX
不使用 X 锁。 SELECT
语句永远不会使用 X 锁。此外,锁定整个 table.
其次,这里即使HOLDLOCK
也不够,因为如果没有修改数据,就不会拿锁。 您必须使用UPDLOCK
才能按预期工作。
老实说,您实际上并不需要 SELECT
,因为整个事情可以在一个原子语句中完成:
I note that
@oldBalance
isn't actually used.
UPDATE BankAccounts WITH (HOLDLOCK)
SET AvailableBalance += @amount,
@newBalance = AvailableBalance + @amount
WHERE Id = @BankAccountId;
您甚至可以使用 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