Quali sono le mie opzioni di blocco per un'istruzione Merge?


13

Ho una procedura memorizzata che esegue una MERGEdichiarazione .

Sembra che blocchi l'intera tabella per impostazione predefinita quando si esegue l'unione.

Sto chiamando questa procedura memorizzata all'interno di una transazione in cui sto facendo anche altre cose e vorrei che bloccasse solo le righe interessate.

Ho provato il suggerimento MERGE INTO myTable WITH (READPAST)e sembrava bloccarsi di meno. Ma c'era un avviso nel documento ms che diceva che poteva inserire chiavi duplicate, bypassando anche la chiave primaria.

Ecco il mio schema di tabella:

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

Ecco la mia procedura memorizzata:

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

Ecco come sto osservando il blocco:

begin tran
EXEC MergeTest 1, 1

E poi in un'altra sessione:

EXEC MergeTest 2, 2

La seconda sessione attende il completamento del primo prima di procedere.


1
WITH (READPAST)indica a SQL Server di saltare solo le righe bloccate da altre sessioni. Sei sicuro di volerlo fare? Inoltre, quante righe in questa tabella stai modificando? Mostraci lo schema della tabella (inclusi gli indici) e l' MERGEistruzione che stai eseguendo.
Nick Chammas,

@NickChammas grazie per l'aiuto, ho aggiornato la domanda con i dettagli. Immagino che READPAST sarebbe un male ...
John Buchanan,

Risposte:


12

È necessario fornire al Query Processor un percorso di accesso più efficiente per individuare i StudentTotalMarksrecord. Come scritto, la query richiede una scansione completa della tabella con un predicato residuo [StudentID] = [@StudentId]applicato a ciascuna riga:

Piano di scansione

Il motore esegue i Ublocchi (aggiorna) durante la lettura come difesa di base contro una causa comune di deadlock di conversione. Questo comportamento indica i blocchi di seconda esecuzione quando si tenta di ottenere un Ublocco sulla riga già bloccato con un Xblocco (esclusivo) dalla prima esecuzione.

Il seguente indice fornisce un percorso di accesso migliore, evitando di eseguire Ublocchi non necessari :

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

Il piano di query ora include un'operazione di ricerca StudentID = [@StudentId], quindi i Ublocchi sono richiesti solo nelle righe di destinazione:

Cerca piano

L'indice non deve necessariamente UNIQUErisolvere il problema (sebbene INCLUDEsia necessario per renderlo un indice di copertura per questa query).

Rendendo StudentIDla PRIMARY KEYdella StudentTotalMarkstavola inoltre risolvere il problema percorso di accesso (e apparentemente ridondante Idcolonna potrebbe essere rimosso). Dovresti sempre applicare chiavi alternative con un vincolo UNIQUEo PRIMARY KEY(ed evitare di aggiungere chiavi surrogate insignificanti senza una buona ragione).

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.