Каковы мои варианты блокировки для оператора слияния?

13

У меня есть хранимая процедура, которая выполняет MERGEзаявление .

Кажется, что при выполнении слияния по умолчанию блокируется вся таблица.

Я вызываю эту хранимую процедуру внутри транзакции, где я также делаю некоторые другие вещи, и я хотел бы, чтобы она блокировала только затронутые строки.

Я попробовал подсказку, MERGE INTO myTable WITH (READPAST)и она, казалось, стала меньше. Но в документе MS было предупреждение о том, что он может вставлять дубликаты ключей, минуя даже первичный ключ.

Вот моя схема таблицы:

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оператор, который вы выполняете.
Ник Чаммас
@NickChammas спасибо за помощь, я уточнил вопрос с деталями. Я предполагаю, что READPAST будет плохим ...
Джон Бьюкенен

Ответы:

12

Вам нужно дать обработчику запросов более эффективный путь доступа для поиска StudentTotalMarksзаписей. Как написано, запрос требует полного сканирования таблицы с остаточным предикатом, [StudentID] = [@StudentId]примененным к каждой строке:

План сканирования

Движок принимает U(обновляет) блокировки при чтении в качестве основной защиты от общей причины тупиков конверсии. Это поведение означает вторые блоки выполнения при попытке получить Uблокировку для строки, уже заблокированной X(исключительной) блокировкой при первом выполнении.

Следующий индекс обеспечивает лучший путь доступа, избегая ненужных Uблокировок:

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

План запроса теперь включает в себя операцию поиска StudentID = [@StudentId], поэтому Uблокировки запрашиваются только для целевых строк:

Искать план

Индекс не обязателен для UNIQUEрешения рассматриваемой проблемы (хотя INCLUDEон необходим, чтобы сделать его индексом покрытия для этого запроса).

Создание из таблицы также позволит решить проблему пути доступа (и , по- видимому лишние колонки могут быть удалены). Вы должны всегда применять альтернативные ключи с ограничением или (и избегать добавления бессмысленных суррогатных ключей без уважительной причины).StudentIDPRIMARY KEYStudentTotalMarksIdUNIQUEPRIMARY KEY

Пол Уайт 9
источник