web-dev-qa-db-ja.com

一括挿入で使用するIDのブロックを予約するためのSQL Serverの慣用的なソリューションは何ですか?

ID列を持つテーブルがあり、一括挿入に使用できる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億行を挿入します。すべてが正常に機能しています。データベースはボトルネックではありません。バッチ処理自体は計算コストが高く、並列で実行するサーバーを2台追加する必要があるため、複数のプロセス「バッチ挿入」に対応する必要があります。同時。

8

次の手順を使用できます(SQL Server 2012で導入)。
sp_sequence_get_range

これを使用するには、SEQUENCEオブジェクトを作成し、IDENTITY列の代わりにそれをデフォルト値として使用する必要があります。

例があります:

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; 

ドキュメント: sp_sequence_get_range

13
Piotr