カウンターテーブルを維持しない場合は、2つのオプションがあります。トランザクション内で、最初にMAX(seq_id)
を選択します 次の表のヒントのいずれかを使用します。
-
WITH(TABLOCKX, HOLDLOCK)
-
WITH(ROWLOCK, XLOCK, HOLDLOCK)
TABLOCKX + HOLDLOCK
少しやり過ぎです。 重いと見なすことができる通常のselectステートメントをブロックします トランザクションは小さいですが。
ROWLOCK, XLOCK, HOLDLOCK
テーブルヒントはおそらくより良いアイデアです(しかし:カウンターテーブルで代替案をさらに読んでください)。利点は、通常のselectステートメントをブロックしないことです。つまり、selectステートメントがSERIALIZABLE
に表示されない場合です。 トランザクション、またはselectステートメントが同じテーブルヒントを提供しない場合。 ROWLOCK, XLOCK, HOLDLOCK
を使用する 挿入ステートメントは引き続きブロックされます。
もちろん、プログラムの他の部分がMAX(seq_id)
を選択していないことを確認する必要があります。 これらのテーブルヒントなし(またはSERIALIZABLE
の外側) トランザクション)、この値を使用して行を挿入します。
この方法でロックされている行の数によっては、SQLServerがロックをテーブルロックにエスカレートする可能性があることに注意してください。ロックエスカレーションの詳細については、こちら> 。
WITH(ROWLOCK, XLOCK, HOLDLOCK)
を使用した挿入手順 次のようになります:
DECLARE @target_model INT=3;
DECLARE @part VARCHAR(128)='Spine';
BEGIN TRY
BEGIN TRANSACTION;
DECLARE @max_seq INT=(SELECT MAX(seq) FROM dbo.table_seq WITH(ROWLOCK,XLOCK,HOLDLOCK) WHERE [email protected]_model);
IF @max_seq IS NULL SET @max_seq=0;
INSERT INTO dbo.table_seq(part,seq,model)VALUES(@part,@max_seq+1,@target_model);
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION;
END CATCH
別の、おそらくより良いアイデアは、カウンターを持つことです。 テーブル、およびこれらのテーブルヒントをカウンターテーブルに提供します。このテーブルは次のようになります。
CREATE TABLE dbo.counter_seq(model INT PRIMARY KEY, seq_id INT);
次に、挿入手順を次のように変更します。
DECLARE @target_model INT=3;
DECLARE @part VARCHAR(128)='Spine';
BEGIN TRY
BEGIN TRANSACTION;
DECLARE @new_seq INT=(SELECT seq FROM dbo.counter_seq WITH(ROWLOCK,XLOCK,HOLDLOCK) WHERE [email protected]_model);
IF @new_seq IS NULL
BEGIN SET @new_seq=1; INSERT INTO dbo.counter_seq(model,seq)VALUES(@target_model,@new_seq); END
ELSE
BEGIN SET @new_seq+=1; UPDATE dbo.counter_seq SET [email protected]_seq WHERE [email protected]_model; END
INSERT INTO dbo.table_seq(part,seq,model)VALUES(@part,@new_seq,@target_model);
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION;
END CATCH
利点は、使用される行ロックが少ないことです(つまり、dbo.counter_seq
のモデルごとに1つ) )、およびロックエスカレーションはdbo.table_seq
全体をロックすることはできません したがって、テーブルはselectステートメントをブロックします。
WAITFOR DELAY '00:01:00'
を配置することで、これらすべてをテストして効果を自分で確認できます。 counter_seq
からシーケンスを選択した後 、および2番目のSSMSタブでテーブルをいじる。
PS1:ROW_NUMBER() OVER (PARTITION BY model ORDER BY ID)
を使用する 良い方法ではありません。行が削除/追加された場合、またはIDが変更された場合、順序は変更されます(決して変更されるべきではない請求書IDを考慮してください)。また、パフォーマンスの観点から、単一の行を取得するときに前のすべての行の行番号を決定する必要があるのは悪い考えです。
PS2:SQL Serverが分離レベルまたはきめ細かいテーブルヒントによるロックをすでに提供している場合、外部リソースを使用してロックを提供することは決してありません。