web-dev-qa-db-ja.com

SQL Serverの同時Updlockシリアライズ可能vsキャッチを試行

異なるフラットファイルから毎秒複数のレコードをインポートします。時々、競合状態に遭遇し、一意のエラー制約を複製します。レコードの挿入と取得を行っています。

これには二つの方法があると聞いています。どちらが良い方法なのか、UPDLOCKを聞いた。SERIALIZABLEが標準的な方法だ。ただし、catchを実行すると、追加のIfステートメントをチェックできなくなります。どちらの方法も完全な証拠であり、重複した挿入を停止しますか?コーディングの実践における最良の方法は何ですか。

CREATE TABLE dbo.Customer
(
    RowId bigint IDENTITY(1,1) NOT NULL,
    CustomerId guid NOT NULL,
    Name varchar(255) NOT NULL,
    CONSTRAINT PK_RowId PRIMARY KEY CLUSTERED([RowId] ASC)
)
create unique nonclustered index [UN_CustomerId] ON [dbo].[Customer] ([CustomerId] ASC) include (Name)
create nonclustered index [UN_Name] ON [dbo].[Customer] ([Name] ASC) include (CustomerId)

方法1:

IF NOT EXISTS
(
    SELECT * 
    FROM dbo.Customer WITH (UPDLOCK, SERIALIZABLE) 
    WHERE Name = @Name
)
BEGIN
    INSERT INTO dbo.Customer(CustomerId, Name) VALUES (@CustomerId, @Name)
    SELECT @CustomerId
END
ELSE
BEGIN
    SELECT CustomerId FROM dbo.Customer WHERE Name = @Name
END

方法2:

BEGIN TRY
    INSERT INTO dbo.Customer(CustomerId, Name) VALUES (@CustomerId, @Name)
    SELECT @CustomerId
END TRY
BEGIN CATCH
    SELECT CustomerId FROM dbo.Customer WHERE Name = @Name
END CATCH 
4
user129291

どちらの方法も完全な証拠であり、重複した挿入を停止しますか?

方法2は、記述されている同時実行では安全ではありません。 catch節のselectが実行されても、挿入の失敗の原因となった行が存在し続けるという保証はありません。

さらに、コードはエラー番号をチェックしないため、重複キー違反以外のエラーに対してcatch句が実行される可能性があります。

終了したトランザクション の可能性にも注意する必要があります。

Aaron Bertrand オーバーヘッドについて書いた try/catchの。オーバーヘッドは通常、最初にチェックするよりも高くなります。

コーディングの実践における最良の方法は何ですか。

方法1は一般的なパターンですが、安全のためにトランザクションが必要です。パフォーマンスは地域の要因に依存するため、独自のテストを実施する必要があります。補足として、代わりにoutput句を使用すると、1つのクエリを回避できます。

DECLARE 
    @CustomerId uniqueidentifier = {guid '16D39773-9CC2-4CCF-A6A8-ACF1465030CC'},
    @Name varchar(255) = 'name';

BEGIN TRANSACTION;

    IF NOT EXISTS
    (
        SELECT * 
        FROM dbo.Customer WITH (UPDLOCK, SERIALIZABLE) 
        WHERE Name = @Name
    )
    BEGIN
        INSERT dbo.Customer(CustomerId, [Name])
        OUTPUT @CustomerId AS CustomerId
        VALUES (@CustomerId, @Name);
    END;
    ELSE
    BEGIN
        SELECT CustomerId FROM dbo.Customer WHERE [Name] = @Name;
    END;

COMMIT TRANSACTION;

別の方法として、安全なマージソリューションのパフォーマンスを比較することもできます。

DECLARE 
    @CustomerId uniqueidentifier = {guid '16D39773-9CC2-4CCF-A6A8-ACF1465030CC'},
    @Name varchar(255) = 'name';

MERGE dbo.Customer WITH (SERIALIZABLE) AS C
USING (VALUES(@CustomerId, @Name)) AS I (CustomerId, [Name])
    ON I.Name = C.Name
WHEN NOT MATCHED 
    THEN INSERT (CustomerId, [Name])
    VALUES (I.CustomerId, I.[Name])
WHEN MATCHED THEN UPDATE 
    SET @CustomerId = C.CustomerId,
        @Name = C.[Name]
OUTPUT @CustomerId AS CustomerId;
5
Paul White 9