J'ai une table avec une colonne d'identité et je souhaite réserver un bloc d'identifiants que je peux utiliser pour l'insertion en bloc, tout en permettant aux insertions de continuer à se produire dans cette table.
Notez que cela fait partie d'une insertion groupée de plusieurs tables, où ces autres tables se rapportent à ces identifiants via un FK. Par conséquent, je dois les bloquer afin que je puisse préparer les relations à l'avance.
J'ai trouvé une solution qui fonctionne en prenant un verrou sur la table dans une transaction, puis en réamorçant (ce qui est assez rapide). Mais cela me semble un peu hacky - existe-t-il un modèle généralement accepté pour le faire?
create table dbo.test
(
id bigint not null primary key identity(1,1),
SomeColumn nvarchar(100) not null
)
Voici le code pour bloquer (faire de la place pour) certains identifiants:
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;
Mon code est un traitement par lots de blocs de données en morceaux d'environ 1000. J'ai environ un milliard de lignes à insérer au total. Tout fonctionne bien - la base de données n'est pas le goulot d'étranglement, le traitement par lots lui-même est coûteux en calcul et me oblige à ajouter quelques serveurs pour fonctionner en parallèle, j'ai donc besoin d'accueillir plus d'un processus "insertion par lots" à la en même temps.
Vous pouvez utiliser la procédure (introduite dans SQL Server 2012):sp_sequence_get_range
Pour l'utiliser, vous devez créer un objet SEQUENCE et l'utiliser comme valeur par défaut au lieu de la colonne IDENTITY.
Il y a un exemple:
CREATE SCHEMA Test ;
GO
CREATE SEQUENCE Test.RangeSeq
AS int
START WITH 1
INCREMENT BY 1
CACHE 10
;
CREATE TABLE Test.ProcessEvents
(
EventID int PRIMARY KEY CLUSTERED
DEFAULT (NEXT VALUE FOR Test.RangeSeq),
EventTime datetime NOT NULL DEFAULT (getdate()),
EventCode nvarchar(5) NOT NULL,
Description nvarchar(300) NULL
) ;
DECLARE
@range_first_value_output sql_variant ;
EXEC sp_sequence_get_range
@sequence_name = N'Test.RangeSeq'
, @range_size = 4
, @range_first_value = @range_first_value_output OUTPUT ;
SELECT @range_first_value_output;
Documentation: sp_sequence_get_range