¿Cuáles son mis opciones de bloqueo para una declaración de fusión?

13

Tengo un procedimiento almacenado que realiza una MERGEdeclaración .

Parece que bloquea toda la tabla de forma predeterminada al realizar la fusión.

Llamo a este procedimiento almacenado dentro de una transacción donde también estoy haciendo otras cosas y deseo que solo bloquee las filas afectadas.

Probé la pista MERGE INTO myTable WITH (READPAST)y pareció bloquearse menos. Pero había una advertencia en el documento de ms que decía que podía insertar claves duplicadas, omitiendo incluso la clave primaria.

Aquí está mi esquema de tabla:

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

Aquí está mi procedimiento almacenado:

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

Así es como estoy observando el bloqueo:

begin tran
EXEC MergeTest 1, 1

Y luego en otra sesión:

EXEC MergeTest 2, 2

La segunda sesión espera a que se complete la primera antes de continuar.

John Buchanan
fuente
1
WITH (READPAST)indica a SQL Server que simplemente omita las filas que están bloqueadas por otras sesiones. ¿Estás seguro de que quieres hacer eso? Además, ¿cuántas filas en esta tabla está modificando? Muéstrenos el esquema de la tabla (incluidos los índices) y la MERGEdeclaración que está ejecutando.
Nick Chammas
@NickChammas gracias por la ayuda, actualicé la pregunta con los detalles. Me imagino que READPAST sería malo ...
John Buchanan

Respuestas:

12

Debe proporcionar al procesador de consultas una ruta de acceso más eficiente para ubicar los StudentTotalMarksregistros. Tal como está escrito, la consulta requiere un análisis completo de la tabla con un predicado residual [StudentID] = [@StudentId]aplicado a cada fila:

Plan de escaneo

El motor toma U(actualiza) bloqueos cuando lee como una defensa básica contra una causa común de bloqueos de conversión. Este comportamiento significa que el segundo bloque de ejecución al intentar obtener un Ubloqueo en la fila ya bloqueado con un Xbloqueo (exclusivo) en la primera ejecución.

El siguiente índice proporciona una mejor ruta de acceso, evitando Ubloqueos innecesarios :

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

El plan de consulta ahora incluye una operación de búsqueda activada StudentID = [@StudentId], por lo que los Ubloqueos solo se solicitan en las filas de destino:

Plan de búsqueda

No se requiere que el índice sea UNIQUEpara resolver el problema en cuestión (aunque INCLUDEse requiere para que sea un índice de cobertura para esta consulta).

Haciendo StudentIDel PRIMARY KEYde la StudentTotalMarkstabla también se resolvería el problema del camino de acceso (y el parecer redundante Idcolumna podría ser eliminado). Siempre debe aplicar claves alternativas con una restricción UNIQUEo PRIMARY KEY(y evitar agregar claves sustitutas sin sentido sin una buena razón).

Paul White 9
fuente