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


13

我有一个执行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

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


1
WITH (READPAST)指示SQL Server仅跳过其他会话锁定的行。您确定要这样做吗?另外,您要修改此表中的几行?向我们显示表模式(包括索引)和MERGE您正在运行的语句。
Nick Chammas 2012年

@NickChammas谢谢您的帮助,我用详细信息更新了问题。我想READPAST会不好...
John Buchanan 2012年

Answers:


12

您需要为查询处理器提供更有效的访问路径以查找StudentTotalMarks记录。按照编写的方式,查询需要对表进行全面扫描,并将残余谓词[StudentID] = [@StudentId]应用于每行:

扫描计划

U读取时,引擎采用(更新)锁作为对转换死锁的常见原因的基本防御。此行为意味着,第二个执行块在尝试通过第一个执行获得U对已用X(独占)锁锁定的行的锁时,会阻塞。

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

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

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

寻求计划

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

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.