sql >> データベース >  >> RDS >> Sqlserver

シーケンスで次の番号を取得する方法

    カウンターテーブルを維持しない場合は、2つのオプションがあります。トランザクション内で、最初にMAX(seq_id)を選択します 次の表のヒントのいずれかを使用します。

    1. WITH(TABLOCKX, HOLDLOCK)
    2. 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が分離レベルまたはきめ細かいテーブルヒントによるロックをすでに提供している場合、外部リソースを使用してロックを提供することは決してありません。



    1. PHPを使用してOracleデータベースにANSI特殊文字を保存します

    2. 一意のインデックス、varchar列、および(空白)スペースの動作

    3. データベースへのプリペアドステートメントの挿入-PSQL

    4. PostgreSQL(psql)でヌル出力の現在の設定を表示する方法