Las respuestas de @Kin, @AaronBertrand y @DBAFromTheCold son geniales y fueron muy útiles. Una pieza importante de información que encontré durante las pruebas que las otras respuestas omitieron es que debe usar el índice que se devuelve sys.partitions
para lo dado HOBT_ID
al buscar el %%lockres%%
(a través de una pista de consulta de índice). Este índice no siempre es PK o índice agrupado.
Por ejemplo:
--Sometimes this does not return the correct results.
SELECT lockResKey = %%lockres%% ,*
FROM [MyDB].[dbo].[myTable]
WHERE %%lockres%% = @lockres
;
--But if you add the index query hint, it does return the correct results
SELECT lockResKey = %%lockres%% ,*
FROM [MyDB].[dbo].[myTable] WITH(NOLOCK INDEX([IX_MyTable_NonClustered_index]))
WHERE %%lockres%% = @lockres
;
Aquí hay un script de ejemplo modificado usando piezas de cada una de estas respuestas.
declare @keyValue varchar(256);
SET @keyValue = 'KEY: 5:72057598157127680 (92d211c2a131)' --Output from deadlock graph: process-list/process[waitresource] -- CHANGE HERE !
------------------------------------------------------------------------
--Should not have to change anything below this line:
declare @lockres nvarchar(255), @hobbitID bigint, @dbid int, @databaseName sysname;
--.............................................
--PARSE @keyValue parts:
SELECT @dbid = LTRIM(SUBSTRING(@keyValue, CHARINDEX(':', @keyValue) + 1, CHARINDEX(':', @keyValue, CHARINDEX(':', @keyValue) + 1) - (CHARINDEX(':', @keyValue) + 1) ));
SELECT @hobbitID = convert(bigint, RTRIM(SUBSTRING(@keyValue, CHARINDEX(':', @keyValue, CHARINDEX(':', @keyValue) + 1) + 1, CHARINDEX('(', @keyValue) - CHARINDEX(':', @keyValue, CHARINDEX(':', @keyValue) + 1) - 1)));
SELECT @lockRes = RTRIM(SUBSTRING(@keyValue, CHARINDEX('(', @keyValue) + 0, CHARINDEX(')', @keyValue) - CHARINDEX('(', @keyValue) + 1));
--.............................................
--Validate DB name prior to running dynamic SQL
SELECT @databaseName = db_name(@dbid);
IF not exists(select * from sys.databases d where d.name = @databaseName)
BEGIN
RAISERROR(N'Database %s was not found.', 16, 1, @databaseName);
RETURN;
END
declare @objectName sysname, @indexName sysname, @schemaName sysname;
declare @ObjectLookupSQL as nvarchar(max) = '
SELECT @objectName = o.name, @indexName = i.name, @schemaName = OBJECT_SCHEMA_NAME(p.object_id, @dbid)
FROM ' + quotename(@databaseName) + '.sys.partitions p
JOIN ' + quotename(@databaseName) + '.sys.indexes i ON p.index_id = i.index_id AND p.[object_id] = i.[object_id]
JOIN ' + quotename(@databaseName)+ '.sys.objects o on o.object_id = i.object_id
WHERE hobt_id = @hobbitID'
;
--print @ObjectLookupSQL
--Get object and index names
exec sp_executesql @ObjectLookupSQL
,N'@dbid int, @hobbitID bigint, @objectName sysname OUTPUT, @indexName sysname OUTPUT, @schemaName sysname OUTPUT'
,@dbid = @dbid
,@hobbitID = @hobbitID
,@objectName = @objectName output
,@indexName = @indexName output
,@schemaName = @schemaName output
;
DECLARE @fullObjectName nvarchar(512) = quotename(@databaseName) + '.' + quotename(@schemaName) + '.' + quotename(@objectName);
SELECT fullObjectName = @fullObjectName, lockIndex = @indexName, lockRes_key = @lockres, hobt_id = @hobbitID, waitresource_keyValue = @keyValue;
--Validate object name prior to running dynamic SQL
IF OBJECT_iD( @fullObjectName) IS NULL
BEGIN
RAISERROR(N'The object "%s" was not found.',16,1,@fullObjectName);
RETURN;
END
--Get the row that was blocked
--NOTE: we use the NOLOCK hint to avoid locking the table when searching by %%lockres%%, which might generate table scans.
DECLARE @finalResult nvarchar(max) = N'SELECT lockResKey = %%lockres%% ,*
FROM ' + @fullObjectName
+ ISNULL(' WITH(NOLOCK INDEX(' + QUOTENAME(@indexName) + ')) ', '')
+ ' WHERE %%lockres%% = @lockres'
;
--print @finalresult
EXEC sp_executesql @finalResult, N'@lockres nvarchar(255)', @lockres = @lockres;
Tiene hobt_id, por lo que la siguiente consulta identificará la tabla:
A partir de eso, puede ejecutar la siguiente instrucción para identificar la fila en la tabla (si todavía existe):
Sin embargo, tenga cuidado con la declaración anterior, escaneará la tabla de destino, por lo que se ejecutará en LEER NO COMPROMETIDO y monitoreará su servidor.
Aquí hay un artículo de Grant Fritchey sobre %% LOCKRES %% - http://www.scarydba.com/2010/03/18/undocumented-virtual-column-lockres/
Y aquí hay un artículo de mi propio blog sobre el uso de %% LOCKRES %% para identificar filas de un evento extendido: - https://dbafromthecold.wordpress.com/2015/02/24/identifying-blocking-via-extended-events/
fuente
El es un complemento de las respuestas ya publicadas por DBAFromTheCold y Aaron Bertrand .
Microsoft todavía se ha ido
%%lockres%%
como característica no documentada .A continuación se muestra el script que lo ayudará :
Consulte también esta excelente publicación de blog sobre: El curioso caso del estancamiento dudoso y el bloqueo no tan lógico
fuente
Lo siento, ya estaba trabajando en esta respuesta y a punto de publicar cuando apareció la otra. Agregar como wiki de la comunidad solo porque es un enfoque ligeramente diferente y agrega un poco de otra información.
El
543066506c7c
es esencialmente un hash de la clave primaria, y se puede recuperar esa fila (y, potencialmente, cualquier fila con una colisión de hash) utilizando este SQL dinámico:Puede hacer esto sin SQL dinámico, por supuesto, pero esto le brinda una buena plantilla para un fragmento o procedimiento almacenado en el que simplemente puede conectar los valores, si esto es algo que soluciona mucho. (También podría parametrizar el nombre de la tabla, y también podría incorporar el análisis de la cadena KEY: para determinar todo de forma dinámica, pero pensé que podría estar fuera del alcance de esta publicación).
fuente