使用子查询时的结果不一致

时间:2016-11-27 12:35:32

标签: sql-server sql-server-2008-r2

鉴于以下正在运行的事务:

begin tran t
select 1 from T(updlock) where id = 'id1'
waitfor delay '00:00:10'
update T set value = 'new' where id = 'id1'
commit tran t

如果在waitfor延迟时间内,我发起以下请求:

select value from T(updlock) where id = 'id1'

此请求将阻塞,直到事务t完成,然后按预期返回结果new

但如果我改为启动以下请求:

select value from T where id in (select id from T(updlock) where id = 'id1')

t也阻止了此请求,但结果为old

为什么?

附件:执行计划

1 个答案:

答案 0 :(得分:1)

这完全有可能。我不确定你为什么认为它不会。 S locks are not blocked by U locks

SELECT T1.value
FROM   T T1
WHERE  T1.id IN (SELECT T2.id
                 FROM   T(updlock) T2
                 WHERE  T2.id = 'id1') 

有一个implied predicate,但这与您看到的行为无关。执行计划与下面相同(将'id1'的搜索复制到T1分支和不相关的子查询)

SELECT T1.value
FROM   T T1
WHERE  T1.id = 'id1'
       AND EXISTS (SELECT *
                   FROM   T(updlock) T2
                   WHERE  T2.id = 'id1') 

enter image description here

第一个窗口中的SELECT查询会在行上显示UPDLOCK并保留10秒钟 - 但这与S搜索所需的T1锁兼容1}}在窗口2中,以便查找使用value列向嵌套循环运算符发出一行。然后嵌套循环从其第二个输入(T2上的搜索)请求一行。这有UPDLOCK提示,因此被阻止。

最终,第一个窗口将其UPDLOCK转换为X锁定,释放其锁定,并且T2上的搜索被解除阻止。然后它会发出一行(没有列,所以完全不受value已更改的事实的影响)。这意味着满足半连接条件(EXISTS谓词),并将原始行传递给根迭代器并输出。