何时在SQL Server中使用事务

时间:2012-02-16 19:34:06

标签: tsql transactions try-catch

关于如何使用交易有很多很多问题。我想知道的是 WHEN ?在什么情况下?什么类型的查询? Try-Catch块可以代替吗?等...

我设计了一个包含~20个表和~20个存储过程的数据库。目前我的SP中没有一个使用事务,但整个过程中有许多Try-Catch块。原因是因为每当我试图将它们包装在一个事务中时,SP就会停止运行,我最终会丢失数据并且比我使用Trans更糟糕。

再次......

  1. 何时适合使用交易?
  2. 作为后续问题,如果我使用它们,我怎么能以这样的方式使用它们,以便只防止其他SP同时访问相同的数据以防止损坏而不是导致我的SP根本不起作用?
  3. 这是我为重命名产品而写的一个小样本SP:

    CREATE PROCEDURE spRenameProduct
        @pKey int = NULL,
        @pName varchar(50)
    AS
    BEGIN
        BEGIN TRY
            IF LTRIM(RTRIM(@pName)) = '' SET @pName = NULL
            IF NOT @pKey IS NULL AND NOT @pName IS NULL BEGIN
                declare @pKeyExisting int = (select MIN(ID) from rProduct where Product like @pName and not ID = @pKey)
                IF @pKeyExisting is null BEGIN
                    update rProduct set IsValid = 1, Product = @pName where ID = @pKey
                END ELSE BEGIN
                    update Request set ProductID = @pKeyExisting where ProductID = @pKey
                    update StatusReport set ProductID = @pKeyExisting where ProductID = @pKey
                    delete from rProduct where ID = @pKey
                END
            END
        END TRY BEGIN CATCH END CATCH
    END
    

    现在如果两个人在同一时间使用它会怎么样?我真的不想,也没有时间(不幸的是),想要开心。吻。在这种情况下是最好的。 :)

5 个答案:

答案 0 :(得分:34)

当您正在进行的数据库操作集需要原子时,您可以使用事务。

那就是 - 他们都需要成功失败。介于两者之间。

将使用事务来确保数据库始终处于一致状态。

一般情况下,除非有充分的理由不使用它们(例如长时间运行的过程),否则请使用它们。有关详细信息,请参阅this blog post


Try/Catch块与事务无关 - 它们用于异常处理。这两个概念没有关系,也没有相互替代。

答案 1 :(得分:1)

常见的答案是事务允许数据库操作是原子的。困惑在于这意味着什么。它不是关于涉及的特定操作,无论它们是SELECT,UPDATE,DELETE等。它是关于数据本身的语义含义。从操作的角度来看,从下到上,我们说作为一个群体,它们是原子的。但是,从抽象层面看,从上到下,我们说我们有信息保护。

一个简单的例子就是如果你有2个账户而且你不希望在他们之间的转账中创造或销毁资金。另一个更微妙的例子是,如果你有一组数据需要作为一个组创建或销毁。换句话说,拥有部分信息并不合理。我想一个例子可能是你有一个用户,并希望始终保证他们有名字和姓氏。不是部分名称。

话虽如此,人们想出了一些短语和经验法则来表达原子意味着什么,例如"操作都需要成功或失败"。此外,人们倾向于注意模式,例如SELECT不需要交易。

答案 2 :(得分:1)

简单的答案是,如果您在一个操作/功能中具有多个更新或插入查询,则使用事务。

答案 3 :(得分:0)

在结束本节“数据操作语言”命令之前,还有另外两个命令,它们非常有用。

在明确提交之前,INSERT,UPDATE和DELETE命令对数据库所做的更改是临时的。这是通过以下命令执行的:

COMMIT;

执行此命令后,您对数据库所做的所有更改都将变为永久性,无法撤消。

从SQL * Plus正常退出时,将自动执行COMMIT。但是,偶尔发出COMMIT命令也没有害处。

COMMIT不适用于任何SELECT命令,因为没有要提交的内容。

COMMIT不适用于任何DDL命令(例如CREATE TABLE,CREATE INDEX等)。这些是自动提交的,无法回滚。

如果您希望回滚(即撤消)自上一次提交以来对数据库所做的任何更改,则可以发出以下命令:

回滚;

一组必须全部成功完成或以其他方式回滚的相关SQL命令称为事务。您对结果3的研究的一部分包括调查事务处理以及回滚和提交的含义。

此外,在插入,更新和删除过程中,RDBMS需要保留数据库的完整性并允许多用户访问(即并发)。尚未提交的事务需要对用户透明。例如,另一个用户不应该访问未提交的插入。同样,试图更新同一记录的两个用户也不应互相妨碍。这需要进行管理。并发由RDBMS使用锁定策略进行管理。您对结果3的研究的一部分包括研究涵盖列,行字段和表锁的锁定策略。

答案 4 :(得分:0)

这篇文章是搜索网络时的第一个结果。共识或多或少会发生:除非在某些情况下,否则始终使用交易。事实上,答案恰恰相反。由于误用或糟糕的设计或简单的运气不好而导致的显式事务不仅会削弱数据库,还会削弱整个服务器甚至整个基础设施。我已经见过了。 我们有几种情况:

a) 交易根本没有帮助。 根据一些答案和链接的博客文章,我们应该使用它以防万一。为什么?什么对你没有帮助,是在路上,摆脱它。所以不,不要把交易放在“以防万一”。

b) 交易可能有一些用处,你有一些疑问。 检查你的代码和你的数据,这笔交易的价值是多少?你的工作是知道。因为不确定而离开事务意味着您需要更多地处理您的 sql 技能和手头的问题。

c) 我需要这笔交易。真的,我是认真的。现在的问题是,我可以删除它吗?我怎样才能尽量减少它的影响? 如果您有简单的插入/更新/删除操作,请使用 MERGE。 MERGE 是原子操作,不需要显式事务。

假设我们有任何需要原子化的任务。我们有一段代码来执行该任务。在这段代码中添加一个开始/提交/回滚事务就足够了吗?

我会让你选择。但答案是否定的。 需要原子的是操作的结果。不是执行我们手头上的一段代码。 我们需要检查代码。评估一下。并且大部分时间都重写它。需要进入事务内部的唯一操作是需要原子的操作。不是创建临时表或读取 XML 文件。

也许我们根本不需要原子操作。我们在某个关键表中有一个 RowStatus 列吗?如果我们这样做,我们可以将我们的行标记为 RowStatus = 'Pending' 或 'In Process' 或 'Step 14'。你想放置交易的那个程序,它是幂等的吗?如果不是,您必须更改它(有或没有事务)。如果它没有事务,它必须可以安全地连续运行 n 次,并且每次运行都必须继续前一次的工作,或者如果作业完成则什么也不做。如果真的不需要原子,那么不需要从头开始(没有显式事务)的幂等过程是最好的选择。我们可以处理以下所有内容:

DO THIS WHERE RowStatus = Something. 

最后,在确认一切正常后(可能使用交易),我们进行最后的更新:

Update Table1 SET RowStatus ='Done' WHERE RowStatus ='Almost Done';
Update Table2 SET RowStatus ='Done' WHERE RowStatus ='Almost Done'
etc.

最后的情况你需要在不同的表中做很多事情,然后是一些。整个事情必须是原子的。

选项 1) 使用触发器。是的,触发器。只是不要将显式事务放入其中。触发器的名声很差,属于它们的公平份额是触发器在事务中执行(这就是为什么您可以在触发器内说回滚事务而不启动任何事务)。

选项 2) 重写您的程序/脚本。在 BD 中的临时表、变量或真实表中进行所有远程捕获/读取、计算和繁重的工作(为此目的在数据库中创建临时、暂存、任何架构)。没有交易。如果您需要复制所有数据库,就这样吧。添加一些 Try/Catch 并检查以确保一切正常。最后,开始尝试开始事务,做你的原子操作(在这一点上,这将是所有琐碎的操作)。并相应地回滚/提交。 示例:

如果你有这个:

BEGIN TRY
    BEGIN TRANSACTION;
    --All my stuff
    IF @MyCheck = 'Error'
        THROW 51000, 'MyError', 1;
    IF @@TranCount > 0 COMMIT TRANSACTION;
END TRY
BEGIN CATCH
    --Other stuff
    IF @@TranCount > 0 ROLLBACK TRANSACTION;
END CATCH;

您需要重构为:

DECLARE @SomeThing int
DROP TABLE IF EXISTS #MyTable
CREATE TABLE #MyTable(MyColumn int);

TRUNCATE TABLE Temp.MyOtherTable
DELETE TABLE  Temp.MyOtherTable2 WHERE x IS NULL;
/*My Heavy stuff*/
--Bring data from linked server to #MyTable
--Bla bla etc

BEGIN TRY
    BEGIN TRANSACTION;
    MERGE MyRealTable T USING #MyTable... 
    MERGE MyRealTable2 T USING Temp.MyOtherTable... 
    IF @MyCheck = 'Error' THROW 51000, 'MyError', 1;
    IF @@TranCount > 0 COMMIT TRANSACTION;
END TRY
BEGIN CATCH
    --My catch stuff
    IF @@TranCount > 0  ROLLBACK TRANSACTION;
END CATCH;

备注:切勿在事务内部(或触发器内部)调用存储过程。或者非常小心。进一步修改此过程可能会破坏性能或造成严重的问题。

相关问题