EXISTS查询是否仍会锁定表格?

时间:2009-08-13 21:24:27

标签: sql-server sql-server-2000

IF NOT EXISTS(SELECT * FROM MyTable WITH(nolock) WHERE Key = 'MyKey')
  INSERT MyTable(Key) Values('MyKey')

如果表中不存在该值,查询是否会获得锁定?

4 个答案:

答案 0 :(得分:6)

来自the docs

  

READUNCOMMITTED和NOLOCK提示仅适用于数据锁。所有查询(包括具有READUNCOMMITTED和NOLOCK提示的查询)在编译和执行期间都会获取Sch-S(模式稳定性)锁。因此,当并发​​事务在表上持有Sch-M(模式修改)锁时,将阻止查询。例如,数据定义语言(DDL)操作在修改表的模式信息之前获取Sch-M锁。尝试获取Sch-S锁时,任何并发查询(包括使用READUNCOMMITTED或NOLOCK提示的查询)都会被阻止。相反,持有Sch-S锁的查询会阻止尝试获取Sch-M锁的并发事务。有关锁定行为的详细信息,请参阅锁定兼容性(数据库引擎)。

因此它不会获取数据锁,但仍会获得模式稳定性锁。

答案 1 :(得分:2)

EXISTS通常仍会获得锁定。但是你添加了一个提示,告诉它不要,所以它不会。

答案 2 :(得分:0)

使用NOLOCK提示确实会阻止行锁定。尽管如此,这种“查找和插入”仍然存在问题。该操作不是原子操作,尝试执行此操作的两个会话将导致竞争条件,当两者都发现密钥丢失并且两者都尝试插入时,导致其中一个导致重复密钥违规。它是否也是次优的,因为索引搜索发生两次(一次查找键,一次查找插入位置)。最佳和正确的解决方案是实际尝试插入并从重复键错误中恢复(如果已存在)。

答案 3 :(得分:0)

该代码容易出错。相反,你可以尝试:

在表上放置一个唯一索引,以便可能插入多个冲突的行,然后插入。冲突会产生错误,您需要处理该错误。

或者,如果冲突是预期的条件而不是异常,那么您将要进行插入/检查原子:

insert MyTable( [Key] ) 
  select 'MyKey' 
  where not exists ( 
    select * 
    from MyTable 
    where [Key] = 'MyKey' 
)

另外,请注意,(nolock)和Read Uncommitted不会产生准确的结果。报告等是可以的,但根据使用(nolock)的决定对您的数据采取行动是危险的。