web-dev-qa-db-ja.com

Mergeステートメントのロックオプションは何ですか?

MERGEステートメント を実行するストアドプロシージャがあります。

マージを実行すると、デフォルトでテーブル全体がロックされるようです。

トランザクション内でこのストアドプロシージャを呼び出していますが、他の処理も行っており、影響を受ける行のみをロックしたいと思います。

ヒントMERGE INTO myTable WITH (READPAST)を試しましたが、ロックが少ないようです。しかし、ms docには、主キーでさえもバイパスして重複キーを挿入する可能性があるという警告がありました。

これが私のテーブルスキーマです:

CREATE TABLE StudentDetails
(
StudentID INTEGER PRIMARY KEY,
StudentName VARCHAR(15)
)
GO
INSERT INTO StudentDetails
VALUES(1,'WANG')
INSERT INTO StudentDetails
VALUES(2,'JOHNSON')
GO

CREATE TABLE StudentTotalMarks
(
Id INT IDENTITY PRIMARY KEY,
StudentID INTEGER REFERENCES StudentDetails,
StudentMarks INTEGER
)
GO
INSERT INTO StudentTotalMarks
VALUES(1,230)
INSERT INTO StudentTotalMarks
VALUES(2,255)
GO

これが私のストアドプロシージャです:

CREATE PROCEDURE MergeTest 
    @StudentId int,
    @Mark int
AS  

WITH Params
AS
(
    SELECT @StudentId as StudentId,
        @Mark as Mark
)
    MERGE StudentTotalMarks AS stm
    USING Params p
    ON stm.StudentID = p.StudentId
    WHEN MATCHED AND stm.StudentMarks > 250 THEN DELETE
    WHEN MATCHED THEN UPDATE SET stm.StudentMarks = p.Mark
    WHEN NOT MATCHED THEN
        INSERT(StudentID,StudentMarks)
        VALUES(p.StudentId, p.Mark);
GO

これが私がロックを観察している方法です:

begin tran
EXEC MergeTest 1, 1

そして別のセッションで:

EXEC MergeTest 2, 2

2番目のセッションは、最初のセッションが完了するのを待ってから続行します。

13
John Buchanan

クエリプロセッサに、StudentTotalMarksレコードを見つけるためのより効率的なアクセスパスを与える必要があります。記述されているように、クエリでは、各行に残余述語[StudentID] = [@StudentId]が適用されたテーブルのフルスキャンが必要です。

Scan Plan

エンジンは、変換デッドロックの一般的な原因に対する基本的な防御策として、読み取り時にU(更新)ロックを取得します。この動作は、最初の実行によってU(排他的)ロックですでにロックされている行でXロックを取得しようとすると、2番目の実行がブロックされることを意味します。

次のインデックスは、不必要なUロックの取得を回避し、より優れたアクセスパスを提供します。

CREATE UNIQUE INDEX uq1 
ON dbo.StudentTotalMarks (StudentID) 
INCLUDE (StudentMarks);

クエリプランにStudentID = [@StudentId]に対するシーク操作が含まれるようになったため、Uロックはターゲット行に対してのみ要求されます。

Seek Plan

インデックスは必須ではなく、当面の問題を解決するためにUNIQUEにする必要があります(ただし、このクエリをカバーするインデックスにするためにはINCLUDEが必要です)。

StudentIDテーブルのStudentTotalMarksPRIMARY KEYにすると、アクセスパスの問題も解決します(明らかに冗長なId列を削除できます)。常にUNIQUEまたはPRIMARY KEY制約を使用して代替キーを適用する必要があります(正当な理由なしに無意味な代理キーを追加しないでください)。

12
Paul White 9