【问题标题】:SQL Server: statement in a batch all execute as one transaction?SQL Server:批处理中的语句都作为一个事务执行?
【发布时间】:2018-08-22 17:17:05
【问题描述】:

所以我用一堆 UPDATE 语句运行了一个 SQL 批处理:

UPDATE...;    
UPDATE...;    
UPDATE...;

结果批处理作为单个事务运行,因此创建了许多锁(我们的数据库监控软件清楚地表明这是一个大事务)所以显然它类似于我围绕批处理编写 BEGIN...COMMIT:

BEGIN TRANS;    
UPDATE...;    
UPDATE...;    
UPDATE...;    
COMMIT;

真的是这样吗?批次总是作为一个事务执行吗? (我不在“显式事务”模式下)为什么会这样,是否可以对其进行配置,或者这只是 SQL Server 的行为?

如果我将每个语句都包装在 BEGIN..COMMIT 中,它会改变什么吗,例如:

BEGIN TRANS;
UPDATE...;
COMMIT;     
BEGIN TRANS;
UPDATE...;
COMMIT;     
BEGIN TRANS;
UPDATE...;
COMMIT; 

谢谢!

【问题讨论】:

标签: sql-server


【解决方案1】:

假设您正在从 SSMS 查询窗口执行更新,您可以将代码重构为

BEGIN TRANS;    
UPDATE...;
GO    
UPDATE...;
GO    
UPDATE...;
GO    
COMMIT;

GO 将使每个 UPDATE 按调用顺序在其自己的批处理中优先执行,但在调用最终 COMMIT 之前不会提交。

我已经通过以下示例对此进行了测试,您可以在 SSMS 中自己运行。

SET NOCOUNT ON
GO

IF EXISTS ( SELECT * FROM sys.tables WHERE name = 'BatchTest' )
    DROP TABLE dbo.BatchTest;
GO

CREATE TABLE dbo.BatchTest (
    id INT IDENTITY (1,1) PRIMARY KEY NOT NULL
    , col1 VARCHAR(50)
    , col2 VARCHAR(50)
    , col3 VARCHAR(50)
    , col4 VARCHAR(50)
)
GO

DECLARE @i INT = 1;
WHILE ( @i <= 100 ) BEGIN
    INSERT INTO dbo.BatchTest ( col1 ) VALUES ( 'col1_' + CAST ( @i AS VARCHAR(10) ) );
    SET @i += 1;
END
GO

SELECT * FROM dbo.BatchTest ORDER BY id;

/*
    -- test tran/commit --
    -- manually select and execute the code below after the BatchTest has been created and populated with data --

    SET NOCOUNT ON
    GO

    BEGIN TRAN;
    UPDATE dbo.BatchTest SET col2 = 'col2_' + CAST ( id AS VARCHAR(10) );
    GO
    UPDATE dbo.BatchTest SET col3 = 'col3_' + CAST ( id AS VARCHAR(10) );
    GO
    UPDATE dbo.BatchTest SET col4 = 'col4_' + CAST ( id AS VARCHAR(10) );
    GO
    --COMMIT;
    ROLLBACK;

    SELECT * FROM dbo.BatchTest ORDER BY id;

*/

请注意代码中关于选择和执行 tran/commit 测试的 cmets。

单个 ROLLBACK 撤消当前事务中的所有 UPDATE,无论使用 GO,而 COMMIT 将全部提交。显然,您应该更加努力并针对您的数据进行测试,但这可能有助于解决您的锁定问题。

关于 SQL Server 使用 GO 及其意图的一些后续阅读: https://docs.microsoft.com/en-us/sql/t-sql/language-elements/sql-server-utilities-statements-go?view=sql-server-2017

【讨论】:

  • 谢谢。但我想问的是关于 SQL Server 行为的更多信息:如果我运行批处理——比如一堆 DML 语句——它们是否都默认作为单个事务执行?
  • 另外,在您的第一个代码 sn-p 中,您已将所有内容包装在 BEGIN..COMMIT 中,并在语句之间添加了 Go。你是说在最后的 COMMIT 被调用之前它不会被提交。但是.. 如果我不在那里添加 GO,我会得到相同的结果,不是吗?
  • GO 执行前一个语句并等待它完成,然后再继续下一个。这应该有助于解决您的一些锁定问题,因为并非所有更新都会立即触发。您对 TRAN/COMMIT 的看法是正确的。无论使用 GO,它的工作原理都是一样的。
  • 所以你的意思是,在我的批次中,没有 GO,所有这些 UPDATE 将立即执行,而不是连续执行(但不是作为一个事务,因为我没有明确的BEGIN..COMMIT) 所以 GO 只控制时间
  • 没错。我希望它可能有助于锁定您的服务器资源——而不是 TRAN——但这实际上取决于许多因素,最大的是要更新多少数据。如果有很多行,您需要考虑将它们分成更小的部分。
猜你喜欢
  • 2018-10-26
  • 2015-10-25
  • 2013-01-18
  • 1970-01-01
  • 2014-10-23
  • 1970-01-01
  • 2015-07-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多