围绕单个陈述的交易是做什么的?

时间:2009-07-23 13:31:34

标签: sql sql-server transactions

我理解事务如何对协调一对更新有用。我不明白的是在交易中包装单个陈述,这是我所见过的90%。实际上,在我的经验中,在我的经验中更常见的是找到一系列逻辑相关的事务,每个事务都包含在他们自己的事务中,但整个事务并没有包含在事务中。

在MS-SQL中,在事务中包装单个选择,单个更新,单个插入还是单个删除有什么好处吗?

我怀疑这是迷信编程。

6 个答案:

答案 0 :(得分:58)

它什么都不做。所有单独的SQL语句(除极少数例外,如无日志的批量插入或截断表)都是自动“在事务中”,无论您是否明确说出这种情况......(即使它们插入,更新或删除数百万行) 。

编辑:基于@ Phillip的评论......在当前版本的SQL Server中,Even Bulk Inserts和Truncate Table会将一些数据写入事务日志,尽管没有其他操作那么多做。与事务角度的关键区别在于,在这些其他类型的操作中,正在修改的数据库表中的数据不在日志中,允许其回滚。

所有这些意味着语句对数据库中的数据所做的更改会记录到事务日志中,以便在操作失败时撤消它们。

“Begin Transaction”,“Commit Transaction”和“RollBack Transaction”命令提供的唯一功能是允许您将两个或多个单独的SQL语句放入同一个事务中。

编辑:(加强商标评论......)是的,这可能归因于“迷信”编程,或者它可能表明对数据库交易性质的根本误解。一个更慈善的解释是,它只是过度应用一致性的结果,这是不合适的,而且是艾默生委婉语的另一个例子:

愚蠢的一致性是小脑袋的大人物 小政治家,哲学家和神职人员崇拜

答案 1 :(得分:8)

正如查尔斯布雷塔纳所说,“它什么都不做” - 除了已经完成的任何事情之外什么都没有。

有没有听说过关系数据库的“ACID”要求? “A”代表Atomic,意味着该语句可以完整地工作,也可以不是 - 并且在执行语句时,没有可以在上完成其他查询受该查询影响的数据。 BEGIN TRANSACTION / COMMIT将此锁定功能“扩展”为多个语句完成的工作,但它不会对单个语句添加任何内容。

但是,在修改数据库(插入,更新,删除)时,数据库事务日志始终写入。这不是一种选择,这一事实往往会刺激人们。是的,批量插入和恢复模式很奇怪,但仍然可以写入。

我也会在这里命名 - 删除隔离级别。使用它会影响单个命令,但这样做仍然不会使声明事务包装的查询执行与“独立”查询不同的任何操作。 (请注意,对于多语句声明的事务,它们可能非常强大且非常危险。)另请注意,“nolock”适用于插入/更新/删除 - 这些操作始终需要锁定。< / p>

答案 2 :(得分:4)

对我来说,在事务中包装单个语句意味着如果我在执行手动的一次性UPDATE语句时忘记WHERE子句,我就能够将其回滚。它救了我几次。

e.g。

--------------------------------------------------------------
CREATE TABLE T1(CPK INT IDENTITY(1,1) NOT NULL, Col1 int, Col2 char(3));
INSERT INTO T1 VALUES (101, 'abc');
INSERT INTO T1 VALUES (101, 'abc');
INSERT INTO T1 VALUES (101, 'abc');
INSERT INTO T1 VALUES (101, 'abc');
INSERT INTO T1 VALUES (101, 'abc');
INSERT INTO T1 VALUES (101, 'abc');
INSERT INTO T1 VALUES (101, 'abc');

SELECT * FROM T1


--------------------------------------------------------------
/* MISTAKE SCENARIO     (run each row individually) */
--------------------------------------------------------------
BEGIN TRAN YOUR_TRANS_NAME_1;   /* open a trans named YOUR_TRANS_NAME_1 */
    UPDATE T1 SET COL2 = NULL;  /* run some update statement */
    SELECT * FROM T1;       /* OOPS ... forgot the where clause */
ROLLBACK TRAN YOUR_TRANS_NAME_1;    /* since it did bad things, roll it back */
    SELECT * FROM T1;       /* tans rolled back, data restored. */



--------------------------------------------------------------
/* NO MISTAKES SCENARIO (run each row individually) */
--------------------------------------------------------------

BEGIN TRAN YOUR_TRANS_NAME_2;
    UPDATE T1 SET COL2 = 'CBA' WHERE CPK = 4;   /* run some update statement */
    SELECT * FROM T1;               /* did it correctly this time */

COMMIT TRAN YOUR_TRANS_NAME_2           /* commit (close) the trans */

--------------------------------------------------------------

DROP TABLE T1

--------------------------------------------------------------

答案 3 :(得分:2)

当您启动显式事务并发出DML时,语句锁定的资源将保持锁定状态,并且在您手动提交或回滚之前,事务外部不会显示语句结果。

这是您可能需要或不需要的。

例如,您可能希望向外部世界显示初步结果,同时仍然保持对它们的锁定。

在这种情况下,您启动另一个事务,在第一个提交之前发出锁定请求,从而避免竞争条件

DML语句完成或失败后,会立即提交或回滚隐式事务。

答案 4 :(得分:2)

一个可能的借口是,单个语句可能会导致一堆其他SQL通过触发器运行,并且它们可以防止那里出现坏事,尽管我希望任何DBMS都有使用的常识隐式交易已经以相同的方式进行。

我能想到的另一件事是,某些API允许您禁用自动提交,并且代码的编写只是为了以防有人这样做。

答案 5 :(得分:1)

SQL Server具有一个设置,该设置允许关闭会话的自动提交。对于某些客户端,它甚至是默认设置(请参见https://docs.microsoft.com/en-us/sql/t-sql/statements/set-implicit-transactions-transact-sql?view=sql-server-2017

根据您使用的框架和/或数据库客户端,不将每个单独的命令放入其自己的事务中可能会导致将它们全部集中到一个默认事务中。明确地将它们包装在事务中,可以明确声明其意图,并实际上确保它按照程序员的意图发生,而不管当前的自动提交设置如何,尤其是在没有公司范围的自动提交策略的情况下。

如果在数据库中观察到begin tran / commit tran命令(根据您的评论here),那么框架也有可能代表毫无疑问的程序员生成它们。 (有多少开发人员密切检查其框架生成的SQL代码?)

我希望这仍然有意义,尽管这个问题有些古老。