4

当我从大型 Excel 将数据导入新表时,如果一条记录失败,则不会导入任何内容。我认为这没关系,因为它符合原子性规则。但是,当我修复源数据错误并再次导入时,标识列不是从 1 开始,而是从一个大值开始。

例如

create table #test (id int identity(1,1), name varchar(4) default '')

insert into #test (name) values('1 insert will failed');
select ident_current('#test') as ident_current
insert into #test (name) values('2 insert will failed');
select ident_current('#test') as ident_current

insert into #test (name) values('3 OK');
select ident_current('#test') as ident_current

select * from #test

drop table #test

结果

id          name 
----------- ---- 
3           3 OK

维基百科将ACID描述为如下

原子性

原子性要求每个事务都是“全有或全无”:如果事务的一部分失败,则整个事务失败,数据库状态保持不变。原子系统必须保证每种情况下的原子性,包括电源故障、错误和崩溃。

因此,如果插入失败,SQL Server 似乎不会让数据库状态(标识值)保持不变,那么,这是否违反了 ACID 规则?

顺便说一句,当插入失败时,PostgreSQL 不会让身份(序列)值增长。(更新:仅有时,请参阅评论。不要依赖于此。)。

test=# create table AutoIncrementTest (id serial not null, name varchar(4));
NOTICE:  CREATE TABLE will create implicit sequence "autoincrementtest_id_seq" for serial column "autoincrementtest.id"
CREATE TABLE
test=# insert into autoincrementtest(name) values('12345');
ERROR:  value too long for type character varying(4)
test=# insert into autoincrementtest(name) values('12345');
ERROR:  value too long for type character varying(4)
test=# insert into autoincrementtest(name) values('1234');
INSERT 0 1
test=# select * from autoincrementtest;
 id | name
----+------
  1 | 1234
4

3 回答 3

5

由于身份值不是物理存储在您可以访问的数据库的任何部分中的东西,我不同意这会破坏原子性。如果您不想“破坏原子性”,或者如果您关心间隙(您不应该),还有其他方法可以做到这一点(例如,使用可序列化事务并将 MAX(col)+1 用于新行)。

于 2012-07-19T15:39:31.610 回答
3

是的,所以不要依赖 MSSQL Server 的连续值。

我建议依靠任何引擎本身的连续身份值本身是一种脆弱而幼稚的方法。这可能总是由于后续删除而发生。

我想这种与纯粹的 ACID 合规性的偏差允许在 MS SQL Server 中进行性能优化。

于 2012-07-19T15:32:37.507 回答
1

根据这个公式,原子性保证数据库状态保持不变。问题是我们所说的数据库状态是什么意思。

只要您了解“身份插入”的 SQL 概念,既不声明也不保证身份列是连续的,就没有问题。当您考虑身份插入时,确实需要重新考虑 SQL 保证的内容,但由于我们知道在上述情况下这可能会失败,因此不能真正保证它是 NEXT 值。

在插入之前,标识列的“下一个”值只保证大于当前值——而不是下一个值。这仍然是之后的状态。

于 2012-07-19T15:58:17.783 回答