我有一个存储过程,会对数据库进行大量探测,以确定是否应更新某些记录
每条记录(订单)都有TIMESTAMP
名为[RowVersion]
我将候选记录ID和RowVersions存储在名为@Ids
DECLARE @Ids TABLE (id int, [RowVersion] Binary(8))
我通过以下
获得候选人的数量DECLARE @FoundCount int
SELECT @FoundCount = COUNT(*) FROM @Ids
由于记录可能会从i SELECT
变为我最终尝试UPDATE
时的变化,因此我需要一种方法来检查并发性,ROLLBACK TRANSACTION
是否检查失败
到目前为止我有什么
BEGIN TRANSACTION
-- create new combinable order group
INSERT INTO CombinableOrders DEFAULT VALUES
-- update orders found into new group
UPDATE Orders
SET Orders.CombinableOrder_Id = SCOPE_IDENTITY()
FROM Orders AS Orders
INNER JOIN @Ids AS Ids
ON Orders.Id = Ids.Id
AND Orders.[RowVersion] = Ids.[RowVersion]
-- if the rows updated dosnt match the rows found, then there must be a concurrecy issue, roll back
IF (@@ROWCOUNT != @FoundCount)
BEGIN
ROLLBACK TRANSACTION
set @Updated = -1
END
ELSE
COMMIT
从上面开始,我正在使用存储的UPDATE
过滤[RowVersion]
,这将跳过任何已经更改过的记录(希望如此)
然而,我不太确定我是否正确地使用了TIMESTAMP
的事务或乐观并发,或者是否有更好的方法来实现我的预期目标
答案 0 :(得分:0)
当您选择数据时,请在显式事务内部尝试使用HOLDLOCK和UPDLOCK。它会破坏其他交易的并发性而不是你的交易。
答案 1 :(得分:0)
很难理解你想要实现的逻辑。
但是,如果您绝对必须在过程中执行多个非原子操作并确保整个代码块在运行时不会再次执行(例如,由其他用户执行),请考虑使用{{3} }。
锁定应用程序资源。
您的程序可能与此类似:
CREATE PROCEDURE [dbo].[YourProcedure]
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
BEGIN TRANSACTION;
BEGIN TRY
DECLARE @VarLockResult int;
EXEC @VarLockResult = sp_getapplock
@Resource = 'UniqueStringFor_app_lock',
@LockMode = 'Exclusive',
@LockOwner = 'Transaction',
@LockTimeout = 60000,
@DbPrincipal = 'public';
IF @VarLockResult >= 0
BEGIN
-- Acquired the lock
-- perform your complex processing
-- populate table with IDs
-- update other tables using IDs
-- ...
END;
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION;
END CATCH;
END