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
需要使该查询覆盖索引)。
制作StudentID
的PRIMARY KEY
的的StudentTotalMarks
表也将解决访问路径问题(显然存在冗余Id
列可以被删除)。您应该始终使用UNIQUE
orPRIMARY KEY
约束强制执行备用键(并避免在没有充分理由的情况下添加无意义的代理键)。