83

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

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

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

4

8 回答 8

113

如果您考虑一下,自动增量数字不应该是事务性的。如果其他事务必须等待查看自动编号是否将被使用或“回滚”,它们将被使用自动编号的现有事务阻止。例如,考虑下面我的伪代码,表 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 的自动编号。

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

于 2008-11-11T23:24:16.847 回答
35

如果你依赖于你的身份价值观是无缝的,那么是的——你做错了。代理键的全部意义在于没有商业意义

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

于 2008-11-12T01:11:21.340 回答
16

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

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

于 2008-11-11T23:56:53.270 回答
8

所有其他说不用担心,你应该有差距的海报都是对的。如果该数字具有商业意义,并且该意义不存在空白,则不要使用标识列。

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

DBCC CHECKIDENT('Product', RESEED, 0)

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

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

于 2008-11-14T02:15:24.013 回答
6

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

于 2008-11-11T23:10:40.053 回答
5

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

  • 事务 a 开始并插入

  • 事务 b 开始并插入

  • 交易中止

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

于 2008-11-11T23:12:24.497 回答
1

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

于 2008-11-11T23:22:39.953 回答
1

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

PRAGMA AUTONOMOUS_TRANSACTION;' 
于 2008-11-11T23:31:09.047 回答