插入失败,但标识值增加,这是否违反了原子性规则



从大型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

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

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

我建议,对于任何引擎,依赖连续的身份值本身都是一种脆弱而天真的方法。这可能总是由于随后的删除而发生。

我认为这种对纯粹ACID遵从性的偏离允许在MS SQL Server中进行性能优化。

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

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

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

最新更新