即使有事务回滚,SQL 标识(自动编号)也会增加

Posted

技术标签:

【中文标题】即使有事务回滚,SQL 标识(自动编号)也会增加【英文标题】:SQL Identity (autonumber) is Incremented Even with a Transaction Rollback 【发布时间】:2010-09-21 21:35:08 【问题描述】:

我有一个带有 SQL 插入到 SQL Server 2005 数据库的 .net 事务。该表具有标识主键。

当事务中发生错误时,会调用Rollback()。行插入正确回滚,但是下次我将数据插入表时,标识会增加,就好像回滚从未发生过一样。所以本质上在身份序列中存在差距。有没有办法让Rollback() 方法收回丢失的身份?

我不是以正确的方式接近这个吗?

【问题讨论】:

建议编辑:将“自动编号”更改为“身份”。 Autonumber 是 MS Access 术语,而 Identity 是 SQL Server 中的专有术语。 尚未将帖子标记为答案?你还在等待“你的答案”吗? 最高票数的答案是零尝试实际回答避免这种行为的问题。不管方法是否正确。 【参考方案1】:

据我所知,插入的行要求自动编号,并且在回滚时,该编号将永远丢失。如果您依赖于排序中的自动编号,您可能需要考虑您正在使用的方法。

【讨论】:

【参考方案2】:

我不认为自动编号的键是连续的。事实上,我不认为他们可以被要求:

事务 a 开始并插入

事务 b 开始并插入

交易中止

你得到一个洞。无事可做。

【讨论】:

【参考方案3】:

Muhan 尝试在许多同时执行此事务的连接的上下文中考虑它,而不是一次一个。有些会失败,有些会成功。您希望 SQL Server 专注于在新请求进入时运行它们,而不是维护无间隙的标识列。 IMO it(价值观的差距)绝对不值得花时间在上面。

【讨论】:

【参考方案4】:

如果您考虑一下,自动递增数字不应该是事务性的。如果其他事务必须等待查看自动编号是否将被使用或“回滚”,它们将被使用自动编号的现有事务阻止。例如,考虑我在下面的表 A 中使用 ID 列的自动编号字段的伪代码:

User 1
------------
begin transaction
insert into A ...
insert into B ...
update C ...
insert into D ...
commit


User 2
-----------
begin transaction
insert into A ...
insert into B ...
commit

如果用户 2 的事务在用户 1 之后一毫秒开始,那么他们对表 A 的插入将不得不等待用户 1 的整个事务完成,以查看是否使用了第一次插入 A 的自动编号。

这是一项功能,而不是错误。如果您需要它们紧密顺序,我建议使用另一种方案来生成自动编号。

【讨论】:

从 SQL Server 2012 开始的序列功能可能有助于创建紧密的序列号 - simple-talk.com/sql/learn-sql-server/sql-server-sequence-basics【参考方案5】:

没有。序列实现使用自治事务。在 Oracle 中,自治事务曾经在 dbms 内部,但现在公开供您自己使用(并且经常被错误地使用)

PRAGMA AUTONOMOUS_TRANSACTION;' 

【讨论】:

【参考方案6】:

如果您也DELETE 一行,您的序列中也会出现空白。

序列必须是唯一的,但它们不需要是连续的。它们单调递增的事实只是实施的侥幸。

【讨论】:

【参考方案7】:

如果你依赖于你的身份价值观是无间隙的,那么是的——你做错了。 surrogate key 的全部意义在于没有商业意义

而且,不,没有办法改变这种行为(除了滚动你自己的自动增量,并遭受阻塞其他插入的性能后果)。

【讨论】:

【参考方案8】:

所有其他说不用担心,并且您应该有差距的发帖人都是对的。如果数字有商业含义,并且该含义不存在空白,则不要使用标识列。

仅供参考,如果您出于某种原因确实想要消除差距,大多数数据库都有办法将自动编号重新设置为您选择的编号。这很麻烦,如果您发现自己需要定期这样做,那么您绝对不应该使用自动编号/身份字段,如上所述。但这是在 SQL Server 中执行此操作的代码:

DBCC CHECKIDENT('Product', RESEED, 0)

这将产品表设置为从 1 开始(尽管如果表中有记录,它显然会跳过已经采用的 ID 值。)其他 RDBMS 供应商有自己的语法,但效果大致一样的,所以在系统帮助文件或互联网中查找“reseed identity”或“reseed autonumber”。

再说一遍:这是为特殊场合使用的,不是经常使用的。不要把它放在存储过程中,让我们都过来。

【讨论】:

在我看来,reseed 仅应在删除测试数据以准备实时数据时使用。而且几乎从不在生产系统上。 虽然如果表中有记录,它显然会跳过已经采用的 ID 值” - 我的经验是,情况并非如此,并且当您尝试执行 INSERT(在 SQL Server 2008 上测试)时,您将获得重复的主键约束违规。您需要将其重新设置为表中的最大标识值。它将在下一次 INSERT 时分配下一个值(即预增量)。

以上是关于即使有事务回滚,SQL 标识(自动编号)也会增加的主要内容,如果未能解决你的问题,请参考以下文章

Sql Server - 即使事务回滚也会增加的计数器列

Sql Server - 计数器列,即使事务回滚也会递增

为啥即使在 Spring 服务类的第二个方法中传播 = Propagation.REQUIRES_NEW 时事务也会回滚?

sql 事务自动回滚

mysql 事务中如果有sql语句出错,会导致自动回滚吗?

SQL Server事务执行一半出错是否自动回滚整个事务