即使有事务回滚,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 标识(自动编号)也会增加的主要内容,如果未能解决你的问题,请参考以下文章
为啥即使在 Spring 服务类的第二个方法中传播 = Propagation.REQUIRES_NEW 时事务也会回滚?