Merge 语句的锁定选项是什么?

Joh*_*nan 13 sql-server-2008 sql-server 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
Run Code Online (Sandbox Code Playgroud)

这是我的存储过程:

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
Run Code Online (Sandbox Code Playgroud)

这是我观察锁定的方式:

begin tran
EXEC MergeTest 1, 1
Run Code Online (Sandbox Code Playgroud)

然后在另一个会话中:

EXEC MergeTest 2, 2
Run Code Online (Sandbox Code Playgroud)

第二个会话在继续之前等待第一个会话完成。

Pau*_*ite 13

您需要为查询处理器提供更有效的访问路径来定位StudentTotalMarks记录。如所写,查询需要对表进行全面扫描,并将剩余谓词[StudentID] = [@StudentId]应用于每一行:

扫描计划

引擎U在读取时使用(更新)锁作为对常见转换死锁原因的基本防御。此行为意味着第二次执行会在尝试获取第一次执行时U已使用X(排他)锁锁定的行上的锁时阻塞。

以下索引提供了更好的访问路径,避免了不必要的U锁定:

CREATE UNIQUE INDEX uq1 
ON dbo.StudentTotalMarks (StudentID) 
INCLUDE (StudentMarks);
Run Code Online (Sandbox Code Playgroud)

查询计划现在包含对 的查找操作StudentID = [@StudentId],因此U仅在目标行上请求锁定:

寻求计划

该指数是不是需要UNIQUE解决手头的问题(虽然INCLUDE需要使该查询覆盖索引)。

制作StudentIDPRIMARY KEY的的StudentTotalMarks表也将解决访问路径问题(显然存在冗余Id列可以被删除)。您应该始终使用UNIQUEorPRIMARY KEY约束强制执行备用键(并避免在没有充分理由的情况下添加无意义的代理键)。