B.M*_*B.M 4 locking sql-server-2005 sql-order-by
我需要使用一些SQL表来建立一个队列系统,就像这里描述的那样.那是,因为我需要通过不同的critera过滤排队的项目,在我正在使用的存储过程中
BEGIN TRANSACTION
CREATE TABLE #Temp (ID INT, SOMEFIELD INT)
INSERT INTO #Temp SELECT TOP (@Something) TableA.ID, TableB.SomeField FROM TableA WITH (ROWLOCK, READPAST) INNER JOIN TableB WITH (UPDLOCK, READPAST) WHERE Condition1
INSERT INTO #Temp SELECT TOP (@Something) TableA.ID, TableB.SomeField FROM TableA WITH (ROWLOCK, READPAST) INNER JOIN TableB WITH (UPDLOCK, READPAST) WHERE Condition2
(...)
UPDATE TableB SET SomeField = 1 FROM TableB WITH (ROWLOCK, READPAST) WHERE ID IN (SELECT ID FROM #Temp)
COMMIT TRANSACTION
Run Code Online (Sandbox Code Playgroud)
我ROWLOCK在第一个表和UPDLOCK第二个表中使用,因为在此选择之后,我将TableB仅更新,但我需要确保这些行不会TableA被任何其他并发查询更新.一切顺利,直到我需要ORDER BY在SELECT上面的任何一个中插入一个子句,以便只选择非常具体的ID(我必须真的这样做).会发生什么:
1)没有ORDER BY,两个并发执行按需执行,返回不同的和不重叠的结果; 但是,他们没有返回我想要的结果,因为那些精确的结果超出了每个SELECT陈述的范围.
2)使用ORDER BY和两个并发执行,只有第一个返回结果.第二个不返回任何东西.
我记得在博客上看到,对于这些查询WITH (ROWLOCK, READPAST)和ORDER BY工作,需要在订单中使用的字段上创建索引.我尝试过,但结果相同.我怎样才能解决这个问题?
编辑:例如,如果我有一个TestTable包含字段的表(TestID INT,Value INT)和值"(1,1),(2,2),..."并执行"同时"
BEGIN TRANSACTION
SELECT TOP 2 TestID FROM TestTable WITH (UPDLOCK, READPAST)
WAITFOR DELAY '00:00:05'
COMMIT TRANSACTION
Run Code Online (Sandbox Code Playgroud)
第一次执行返回行(1,2),第二次执行返回(3,4).但是,如果我执行
BEGIN TRANSACTION
SELECT TOP 2 TestID FROM TestTable WITH (UPDLOCK, READPAST) ORDER BY VALUE ASC
WAITFOR DELAY '00:00:05'
COMMIT TRANSACTION
Run Code Online (Sandbox Code Playgroud)
第一个返回(1,2),第二个返回任何内容.为什么是这样?!
正如所料
带有ORDER BY的SELECT,没有ROWLOCK,没有索引会有一个表锁,因为扫描/中间排序可以解决TOP 2.所以第二个会话因为READPAST而跳过整个表
没有ORDER BY的SELECT只是选择任意2行,这恰好是插入的顺序(纯巧合,没有隐含的顺序).这两行被锁定的事实导致第二个会话跳到下一个非锁定行.
SQL Server尝试尽可能保持锁定,但扫描意味着表锁定.现在,这通常不会产生影响(它是一个共享的读锁定),但你也有UPDLOCK,这意味着一个独占锁定的表
所以,你需要这两个
ValueINCLUDE 的索引TestID,使SELECT有效.索引只能修复并发性,但不能保证.在您之前的一个问题中,我将我的答案(在评论中)链接到SQL Server进程队列竞争条件,其中我有所有3个锁定提示