我有一个带有标识列的表,我想保留一个 id 块,我可以将其用于批量插入,同时允许插入仍然发生在该表中。
请注意,这是多个表的批量插入的一部分,其中其他表通过 FK 与这些 ID 相关。因此,我需要将它们排除在外,以便我可以事先准备好关系。
我找到了一个解决方案,它通过在事务中锁定表然后重新播种(这非常快)来工作。但这对我来说看起来有点老套——是否有一个普遍接受的模式来做到这一点?
create table dbo.test
(
id bigint not null primary key identity(1,1),
SomeColumn nvarchar(100) not null
)
下面是屏蔽(为)一些 ID 腾出空间的代码:
declare @numRowsToMakeRoomFor int = 100
BEGIN TRANSACTION;
SELECT MAX(Id) FROM dbo.test WITH ( XLOCK, TABLOCK ) -- will exclusively lock the table whilst this tran is in progress,
--another instance of this query will not be able to pass this line until this instance commits
--get the next id in the block to reserve
DECLARE @firstId BIGINT = (SELECT IDENT_CURRENT( 'dbo.test' ) +1);
--calculate the block range
DECLARE @lastId BIGINT = @firstId + (@numRowsToMakeRoomFor -1);
--reseed the table
DBCC CHECKIDENT ('dbo.test',RESEED, @lastId);
COMMIT TRANSACTION;
select @firstId;
我的代码是以大约 1000 个块为单位批处理数据块。我总共有大约 10 亿行要插入。一切正常——数据库不是瓶颈,批处理本身在计算上很昂贵,需要我添加几台服务器并行运行,所以我需要容纳多个进程“批量插入”同时。
您可以使用过程(在 SQL Server 2012 中引入):
sp_sequence_get_range
要使用它,您需要创建一个 SEQUENCE 对象并将其用作默认值而不是 IDENTITY 列。
有一个例子:
文档:sp_sequence_get_range