Які мої варіанти блокування для заяви 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оператор, який ви запускаєте.
Нік Чаммас

@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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.