Wayne, believe it or not, I've been playing with the script in your blog in the last few days and I have noticed that in some cases the deadlock XML doesn't get shredded correctly.
Basically, it's the COALESCE/CROSS APPLY chain that works only when the same type of locks/waits is found on both processes, but doesn't work in all cases.
This is what I came up with:
IF OBJECT_ID('tempdb..#deadlockinfo') IS NOT NULL
DROP TABLE #deadlockinfo;
DECLARE @DeadlockGraph XML;
SET @DeadlockGraph =
'<deadlock-list>
<deadlock victim="processa2db88">
<process-list>
<process id="processa2db88" taskpriority="0" logused="0" waitresource="PAGE: 5:1:6824253" waittime="10933" ownerId="1493304634" transactionname="implicit_transaction" lasttranstarted="2012-04-04T15:19:42.193" XDES="0x20cf52730" lockMode="S" schedulerid="3" kpid="68992" status="suspended" spid="81" sbid="0" ecid="3" priority="0" trancount="0" lastbatchstarted="2012-04-04T15:19:44.810" lastbatchcompleted="2012-04-04T15:19:44.807" clientapp="jTDS" hostname="hostname" hostpid="123" isolationlevel="read committed (2)" xactid="1493304634" currentdb="5" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128058">
<executionStack>
<frame procname="adhoc" line="1" stmtstart="40" sqlhandle="0x020000007ecd772bc607c482a24c941ad99dd40ef170ae1e">
select pssiteitem0_.REFERENCE_ID as REFERENCE1_37_, pssiteitem0_.CONTEXT_ID as CONTEXT2_37_, pssiteitem0_.SITE_ID as SITE3_37_,
pssiteitem0_.UNPUBLISH_INFO as UNPUBLISH4_37_, pssiteitem0_.VERSION as VERSION37_ from database.dbo.PSX_PUBLICATION_SITE_ITEM
pssiteitem0_, database.dbo.RXSITES pssite1_, database.dbo.PSX_PUBLICATION_DOC pspubitem2_
where pssiteitem0_.REFERENCE_ID=pspubitem2_.REFERENCE_ID and pssiteitem0_.SITE_ID=pssite1_.SITEID and pssiteitem0_.SITE_ID= @P0
and pssiteitem0_.CONTEXT_ID= @P1 and pspubitem2_.STATUS=pspubitem2_.OPERATION
</frame>
</executionStack>
<inputbuf />
</process>
<process id="process21a958748" taskpriority="0" logused="10000" waittime="10430" schedulerid="2" kpid="67784" status="suspended" spid="81" sbid="0" ecid="0" priority="0" trancount="1" lastbatchstarted="2012-04-04T15:19:44.810" lastbatchcompleted="2012-04-04T15:19:44.807" clientapp="jTDS" hostname="hostname" hostpid="123" loginname="LOGIN" isolationlevel="read committed (2)" xactid="1493304634" currentdb="5" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128058">
<executionStack>
<frame procname="adhoc" line="1" stmtstart="40" sqlhandle="0x020000007ecd772bc607c482a24c941ad99dd40ef170ae1e">
select pssiteitem0_.REFERENCE_ID as REFERENCE1_37_, pssiteitem0_.CONTEXT_ID as CONTEXT2_37_, pssiteitem0_.SITE_ID as SITE3_37_,
pssiteitem0_.UNPUBLISH_INFO as UNPUBLISH4_37_, pssiteitem0_.VERSION as VERSION37_ from database.dbo.PSX_PUBLICATION_SITE_ITEM pssiteitem0_,
database.dbo.RXSITES pssite1_, database.dbo.PSX_PUBLICATION_DOC pspubitem2_ where pssiteitem0_.REFERENCE_ID=pspubitem2_.REFERENCE_ID
and pssiteitem0_.SITE_ID=pssite1_.SITEID and pssiteitem0_.SITE_ID= @P0 and pssiteitem0_.CONTEXT_ID= @P1
and pspubitem2_.STATUS=pspubitem2_.OPERATION
</frame>
</executionStack>
<inputbuf>
(@P0 bigint,@P1 int)select pssiteitem0_.REFERENCE_ID as REFERENCE1_37_, pssiteitem0_.CONTEXT_ID as CONTEXT2_37_,
pssiteitem0_.SITE_ID as SITE3_37_, pssiteitem0_.UNPUBLISH_INFO as UNPUBLISH4_37_, pssiteitem0_.VERSION as VERSION37_
from database.dbo.PSX_PUBLICATION_SITE_ITEM pssiteitem0_, database.dbo.RXSITES pssite1_, database.dbo.PSX_PUBLICATION_DOC pspubitem2_
where pssiteitem0_.REFERENCE_ID=pspubitem2_.REFERENCE_ID and pssiteitem0_.SITE_ID=pssite1_.SITEID and pssiteitem0_.SITE_ID= @P0
and pssiteitem0_.CONTEXT_ID= @P1 and pspubitem2_.STATUS=pspubitem2_.OPERATION
</inputbuf>
</process>
<process id="process4e48bc8" taskpriority="0" logused="98960" waitresource="PAGE: 5:1:6968174" waittime="24" ownerId="1493312850" transactionname="implicit_transaction" lasttranstarted="2012-04-04T15:19:52.987" XDES="0x26163eb60" lockMode="IX" schedulerid="8" kpid="63104" status="suspended" spid="90" sbid="0" ecid="0" priority="0" trancount="2" lastbatchstarted="2012-04-04T15:20:06.870" lastbatchcompleted="2012-04-04T15:20:06.863" clientapp="jTDS" hostname="hostname" hostpid="123" loginname="LOGIN" isolationlevel="read committed (2)" xactid="1493312850" currentdb="5" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128058">
<executionStack>
<frame procname="adhoc" line="1" stmtstart="118" sqlhandle="0x020000003e76203748e31becf98a2dd2b3c36d3f9d5e5ae2">
insert into database.dbo.PSX_PUBLICATION_SITE_ITEM (CONTEXT_ID, SITE_ID, UNPUBLISH_INFO, VERSION, REFERENCE_ID) values
( @P0 , @P1 , @P2 , @P3 , @P4 )
</frame>
</executionStack>
<inputbuf>
(@P0 int,@P1 bigint,@P2 varbinary(8000),@P3 int,@P4 bigint)insert into database.dbo.PSX_PUBLICATION_SITE_ITEM
(CONTEXT_ID, SITE_ID, UNPUBLISH_INFO, VERSION, REFERENCE_ID) values ( @P0 , @P1 , @P2 , @P3 , @P4 )
</inputbuf>
</process>
</process-list>
<resource-list>
<pagelock fileid="1" pageid="6824253" dbid="5" objectname="database.dbo.PSX_PUBLICATION_SITE_ITEM" id="lock1b208e400" mode="IX" associatedObjectId="72057594180927488">
<owner-list>
<owner id="process4e48bc8" mode="IX" />
</owner-list>
<waiter-list>
<waiter id="processa2db88" mode="S" requestType="wait" />
</waiter-list>
</pagelock>
<exchangeEvent id="Pipe225726400" WaitType="e_waitPipeGetRow" nodeId="0">
<owner-list>
<owner id="processa2db88" />
</owner-list>
<waiter-list>
<waiter id="process21a958748" />
</waiter-list>
</exchangeEvent>
<pagelock fileid="1" pageid="6968174" dbid="5" objectname="database.dbo.PSX_PUBLICATION_SITE_ITEM" id="locke1d49980" mode="S" associatedObjectId="72057594180927488">
<owner-list>
<owner id="process21a958748" mode="S" />
</owner-list>
<waiter-list>
<waiter id="process4e48bc8" mode="IX" requestType="wait" />
</waiter-list>
</pagelock>
</resource-list>
</deadlock>
</deadlock-list>'
;
DECLARE @deadlock TABLE (
DeadlockID INT IDENTITY PRIMARY KEY CLUSTERED
,DeadlockGraph XML
);
-- use below to load a deadlock trace file
INSERT INTO @deadlock (DeadlockGraph)
VALUES (@DeadlockGraph);
/*
-- use below to load individual deadlocks.
INSERT INTO @deadlock VALUES ('');
-- Insert the deadlock XML in the above line!
-- Duplicate as necessary for additional graphs.
*/
WITH CTE
AS (
SELECT DeadlockID
,DeadlockGraph
FROM @deadlock
)
,Locks (DeadlockID, LockId, ProcessID, LockType, ObjectName, LockDbid, LockMode, LockPtnId)
AS (
-- Merge all of the lock information together.
SELECT DeadlockID, lockinfo.*
FROM CTE
CROSS APPLY CTE.DeadlockGraph.nodes('/deadlock-list/deadlock') AS Lock(list)
CROSS APPLY (
SELECT LockNode.Process.value('@id', 'varchar(200)')
,LockType.Process.value('@id', 'varchar(200)')
,REPLACE(LockNode.Process.value('local-name(.)', 'varchar(50)'), 'lock', SPACE(0))
,LockNode.Process.value('@objectname', 'sysname')
,LockNode.Process.value('@dbid', 'int')
,LockType.Process.value('@mode', 'varchar(10)')
,LockNode.Process.value('@associatedObjectId', 'bigint')
FROM Lock.list.nodes('resource-list/pagelock,resource-list/keylock,resource-list/ridlock,resource-list/objlock,resource-list/objectlock,resource-list/exchangeEvent') AS LockNode(Process)
CROSS APPLY LockNode.Process.nodes('owner-list/owner') AS LockType(Process)
) AS lockinfo(LockId, ProcessID, LockType, ObjectName, dbid, LockMode, associatedObjectId)
WHERE lockinfo.ProcessID IS NOT NULL
)
,Waits (DeadlockID, LockId, ProcessID, WaitType, ObjectName, WaitDbid, WaitMode, WaitPtnId)
AS (
-- Merge all of the wait information together.
SELECT DeadlockID, waitinfo.*
FROM CTE
CROSS APPLY CTE.DeadlockGraph.nodes('/deadlock-list/deadlock') AS Lock(list)
CROSS APPLY (
SELECT WaitNode.Process.value('@id', 'varchar(200)')
,WaitType.Process.value('@id', 'varchar(200)')
,REPLACE(WaitNode.Process.value('local-name(.)', 'varchar(50)'), 'lock', SPACE(0))
,WaitNode.Process.value('@objectname', 'sysname')
,WaitNode.Process.value('@dbid', 'int')
,WaitType.Process.value('@mode', 'varchar(10)')
,WaitNode.Process.value('@associatedObjectId', 'bigint')
FROM Lock.list.nodes('resource-list/pagelock,resource-list/keylock,resource-list/ridlock,resource-list/objlock,resource-list/objectlock,resource-list/exchangeEvent') AS WaitNode(Process)
CROSS APPLY WaitNode.Process.nodes('waiter-list/waiter') AS WaitType(Process)
) AS waitinfo(LockId, ProcessID, WaitType, ObjectName, WaitDbid, WaitMode, associatedObjectId)
WHERE waitinfo.ProcessID IS NOT NULL
)
,Process
AS (
-- get the data from the process node
SELECT CTE.DeadlockID
,[Victim] = CONVERT(BIT, CASE
WHEN Deadlock.Process.value('@id', 'varchar(50)') = Lock.list.value('@victim', 'varchar(50)')
THEN 1
ELSE 0
END)
,[LockMode] = Deadlock.Process.value('@lockMode', 'varchar(10)')
,-- how is this different from in the resource-list section?
[ProcessID] = Deadlock.Process.value('@id', 'varchar(50)')
,[KPID] = Deadlock.Process.value('@kpid', 'int')
,-- kernel-process id / thread ID number
[SPID] = Deadlock.Process.value('@spid', 'int')
,-- system process id (connection to sql)
[SBID] = Deadlock.Process.value('@sbid', 'int')
,-- system batch id / request_id (a query that a SPID is running)
[ECID] = Deadlock.Process.value('@ecid', 'int')
,-- execution context ID (a worker thread running part of a query)
[IsolationLevel] = Deadlock.Process.value('@isolationlevel', 'varchar(200)')
,[WaitResource] = Deadlock.Process.value('@waitresource', 'varchar(200)')
,[LogUsed] = Deadlock.Process.value('@logused', 'int')
,[ClientApp] = Deadlock.Process.value('@clientapp', 'varchar(100)')
,[HostName] = Deadlock.Process.value('@hostname', 'varchar(20)')
,[LoginName] = Deadlock.Process.value('@loginname', 'varchar(20)')
,[TransactionTime] = Deadlock.Process.value('@lasttranstarted', 'datetime')
,[BatchStarted] = Deadlock.Process.value('@lastbatchstarted', 'datetime')
,[BatchCompleted] = Deadlock.Process.value('@lastbatchcompleted', 'datetime')
,[InputBuffer] = Input.Buffer.query('.')
,CTE.[DeadlockGraph]
,es.ExecutionStack
,[QueryStatement] = Execution.Frame.value('.', 'varchar(max)')
,ProcessQty = SUM(1) OVER (PARTITION BY CTE.DeadlockID)
FROM CTE
CROSS APPLY CTE.DeadlockGraph.nodes('/deadlock-list/deadlock') AS Lock(list)
CROSS APPLY Lock.list.nodes('process-list/process') AS Deadlock(Process)
CROSS APPLY Deadlock.Process.nodes('inputbuf') AS Input(Buffer)
CROSS APPLY Deadlock.Process.nodes('executionStack') AS Execution(Frame)
-- get the data from the executionStack node as XML
CROSS APPLY (
SELECT ExecutionStack = (
SELECT ProcNumber = ROW_NUMBER() OVER (
PARTITION BY CTE.DeadlockID
,Deadlock.Process.value('@id', 'varchar(50)')
,Execution.Stack.value('@procname', 'sysname')
,Execution.Stack.value('@code', 'varchar(MAX)')
ORDER BY (SELECT 1)
)
,ProcName = Execution.Stack.value('@procname', 'sysname')
,Line = Execution.Stack.value('@line', 'int')
,SQLHandle = Execution.Stack.value('@sqlhandle', 'varchar(64)')
,Code = LTRIM(RTRIM(Execution.Stack.value('.', 'varchar(MAX)')))
FROM Execution.Frame.nodes('frame') AS Execution(Stack)
ORDER BY ProcNumber
FOR XML PATH('frame')
,ROOT('executionStack')
,TYPE
)
) es
)
-- get the columns in the desired order
SELECT p.DeadlockID
,p.Victim
,p.ProcessQty
,p.LockMode
,p.ProcessID
,p.KPID
,p.SPID
,p.SBID
,p.ECID
,l.LockType
,LockedObject = NULLIF(l.ObjectName, '')
,LockPtnId
,LockDbid
,LockedMode = l.LockMode
,w.WaitType
,WaitObject = NULLIF(w.ObjectName, '')
,WaitPtnId
,WaitDbid
,w.WaitMode
,p.WaitResource
,p.IsolationLevel
,p.LogUsed
,p.ClientApp
,p.HostName
,p.LoginName
,p.TransactionTime
,p.BatchStarted
,p.BatchCompleted
,p.INPUTBUFFER
,p.DeadlockGraph
,p.ExecutionStack
INTO #deadlockinfo
FROM Process p
LEFT JOIN Locks l
ON p.ProcessID = l.ProcessID
AND p.DeadlockID = l.DeadlockID
LEFT JOIN Waits w
ON p.DeadlockID = w.DeadlockID
AND p.ProcessID = w.ProcessID
ORDER BY p.DeadlockId
,p.Victim DESC
,p.ProcessId;
DECLARE @sql nvarchar(max);
SET @sql = (
SELECT queries AS [text()]
FROM (
SELECT 'EXEC ' + QUOTENAME(DB_NAME(LockDbid)) + '.sys.sp_executesql N''' + REPLACE('
UPDATE #deadlockinfo
SET LockedObject = (
SELECT QUOTENAME(DB_NAME()) + ''.'' +
QUOTENAME(OBJECT_SCHEMA_NAME([object_id])) + ''.'' +
QUOTENAME(OBJECT_NAME([object_id]))
FROM sys.partitions
WHERE partition_id = LockPtnId
UNION ALL
SELECT QUOTENAME(DB_NAME()) + ''.'' +
QUOTENAME(OBJECT_SCHEMA_NAME([LockPtnId])) + ''.'' +
QUOTENAME(OBJECT_NAME([LockPtnId]))
FROM sys.objects
WHERE object_id = LockPtnId
);','''', '''''') + '''' + CHAR(10)
FROM #deadLockInfo
WHERE LockedObject IS NULL
UNION ALL
SELECT 'EXEC ' + QUOTENAME(DB_NAME(WaitDbid)) + '.sys.sp_executesql N''' + REPLACE('
UPDATE #deadlockinfo
SET WaitObject = (
SELECT QUOTENAME(DB_NAME()) + ''.'' +
QUOTENAME(OBJECT_SCHEMA_NAME([object_id])) + ''.'' +
QUOTENAME(OBJECT_NAME([object_id]))
FROM sys.partitions
WHERE partition_id = WaitPtnId
UNION ALL
SELECT QUOTENAME(DB_NAME()) + ''.'' +
QUOTENAME(OBJECT_SCHEMA_NAME([WaitPtnId])) + ''.'' +
QUOTENAME(OBJECT_NAME([WaitPtnId]))
FROM sys.objects
WHERE object_id = WaitPtnId
);','''', '''''') + '''' + CHAR(10)
FROM #deadLockInfo
WHERE WaitObject IS NULL
) AS src(queries)
FOR XML PATH('')
);
SET @sql = REPLACE(@sql, ' ', CHAR(13));
EXEC sp_executesql @sql;
SELECT *
FROM #deadLockInfo;
Hope this helps,
Gianluca
P.S.: thanks for the great script!
-- Gianluca Sartori