SQL Server包装选择...将查询插入隐式事务吗?

时间:2009-07-01 20:40:49

标签: sql-server tsql transactions

当我执行select / Insert查询时,SQL Server是否会自动创建一个隐式事务,从而将其视为一个原子操作?

如果某个值尚未存在,请使用以下查询将值插入表中:

INSERT INTO Table1 (FieldA)
SELECT 'newvalue' 
WHERE NOT EXISTS (Select * FROM Table1 where FieldA='newvalue')

在WHERE子句的评估和INSERT子句的执行之间,如果没有明确地包装在事务中,是否有可能由另一个用户将'newvalue'插入到表中?

4 个答案:

答案 0 :(得分:4)

您在交易和锁定之间感到困惑。如果有任何错误,事务会将您的数据恢复为原始状态。如果不是,它会将数据移动到新状态。在操作交易时,您永远不会将数据置于间歇状态。另一方面,锁定是允许或阻止多个用户同时访问数据的锁定。要回答你的问题,请选择... insert is atomic,只要没有显式请求粒度锁,当select..insert正在进行时,其他用户就无法插入。

答案 1 :(得分:3)

John,答案取决于您当前的隔离级别。如果您设置为READ UNCOMMITTED,您可能正在寻找麻烦,但是如果隔离级别较高,则不应在select和insert之间的表中获得其他记录。使用READ COMMITTED(默认值),REPEATABLE READ或SERIALIZABLE隔离级别,您应该受到保护。

答案 2 :(得分:3)

一个非常常见的问题。在这里解释:

Defensive database programming: eliminating IF statements

答案 3 :(得分:0)

使用 SSMS 2016 ,可以验证Select/Insert语句是否请求锁定(因此最有可能以原子方式运行):

  1. 为以下事务打开一个新的查询/连接,并在启动调试器之前在ROLLBACK TRANSACTION上设置一个断点:

    BEGIN TRANSACTION     
    INSERT INTO Table1 (FieldA) VALUES ('newvalue');    
    ROLLBACK TRANSACTION --[break-point]
    
  2. 在上述断点处,从单独的查询窗口执行以下操作以显示任何锁定(可能需要几秒钟才能注册任何输出):

    SELECT * FROM sys.dm_tran_locks
     WHERE resource_database_id = DB_ID()
       AND resource_associated_entity_id = OBJECT_ID(N'dbo.Table1');
    

    上面的BEGIN TRANSACTION / INSERT应该有一个锁(因为默认情况下会在ISOLATION LEVEL READ COMMITTED中运行

    OBJECT      **  **********  *   IX  LOCK    GRANT   1   
    
  3. SSMS 的另一个实例中,打开一个新查询并运行以下内容(在上述断点处仍然停止):

    INSERT INTO Table1 (FieldA)
    SELECT 'newvalue'
    WHERE NOT EXISTS (Select * FROM Table1 where FieldA='newvalue') 
    

    这应该在查询窗口的标签标题中显示字符串“(正在执行)...”(因为@@LOCK_TIMEOUT默认为-1)。

  4. 第2步

    重新运行查询

    现在应该显示与Select/Insert对应的另一个锁:

    OBJECT      **  **********  0   IX  LOCK    GRANT   1
    OBJECT      **  **********  0   IX  LOCK    GRANT   1
    
  5. 参考: How to check which locks are held on a table