DECLARE @blockedProcessChains TABLE(chainid DATETIME, chainxml xml);
DECLARE @bprXML xml,
@chainXML xml,
@newResourceXml xml,
@newOwnerXml xml,
@newWaiterXml xml;
DECLARE @blockingspid INT,
@blockingecid INT,
@blockingid VARCHAR(30),
@blockingprocess xml,
@blockedspid INT,
@blockedecid INT,
@blockedid VARCHAR(30),
@blockedprocess xml,
@chainid DATETIME,
@waitresource NVARCHAR(50)
SET NOCOUNT ON
DECLARE bpCursor CURSOR
FOR SELECT CAST(textData AS xml), endtime
FROM blocked --hey, remember to save to a trace table in this database named blocked
WHERE eventClass = 137
OPEN bpCursor
FETCH NEXT FROM bpCursor INTO @bprXML, @chainid
WHILE @@FETCH_STATUS = 0
BEGIN
--retrieve info
SELECT
@blockingspid = @bprXML.value('/blocked-process-report[1]/blocking-process[1]/process[1]/@spid', 'int'),
@blockingecid = @bprXML.value('/blocked-process-report[1]/blocking-process[1]/process[1]/@ecid', 'int'),
@blockingprocess = CAST(@bprXML.query('/blocked-process-report[1]/blocking-process[1]/process[1]') AS xml),
@blockedspid = @bprXML.value('/blocked-process-report[1]/blocked-process[1]/process[1]/@spid', 'int'),
@blockedecid = @bprXML.value('/blocked-process-report[1]/blocked-process[1]/process[1]/@ecid', 'int'),
@blockedprocess = CAST(@bprXML.query('/blocked-process-report[1]/blocked-process[1]/process[1]') AS xml),
@waitresource = @bprXML.value('/blocked-process-report[1]/blocked-process[1]/process[1]/@waitresource', 'nvarchar(50)')
--update process ids
SET @blockingid = 'process' + CAST(@blockingspid AS VARCHAR(10)) + '_' + CAST(@blockingecid AS VARCHAR(10))
SET @blockedid = 'process' + CAST(@blockedspid AS VARCHAR(10)) + '_' + CAST(@blockedecid AS VARCHAR(10))
SET @blockingprocess.modify('insert attribute id {sql:variable("@blockingid")} into (/process[1])')
SET @blockedprocess.modify('replace value of (/process/@id)[1] with sql:variable("@blockedid")')
--find chain
SET @chainXml = CAST('' AS xml);
IF EXISTS (SELECT chainid FROM @blockedProcessChains WHERE chainid = @chainid)
SELECT @chainXML = chainxml
FROM @blockedProcessChains
WHERE chainid = @chainid;
ELSE
INSERT @blockedProcessChains(chainid, chainxml)
valueS (@chainid, @chainXML);
--find blocked process (add or replace)
IF (@chainXML.exist('//process-list/process[@ecid = sql:variable("@blockedecid") and @spid = sql:variable("@blockedspid")]') = 1)
SET @chainXML.modify('delete //process-list/process[@ecid = sql:variable("@blockedecid") and @spid = sql:variable("@blockedspid")]')
SET @chainXML.modify('insert sql:variable("@blockedprocess") into (//process-list)[1] ')
--find blocking process (add if needed)
IF NOT (@chainXML.exist('//process-list/process[@ecid = sql:variable("@blockingecid") and @spid = sql:variable("@blockingspid")]') = 1)
SET @chainXML.modify('insert sql:variable("@blockingprocess") into (//process-list)[1] ')
--find resource (add resource if needed)
IF NOT (@chainXML.exist('//resource-list/unknownlock[@resource=sql:variable("@waitresource")]') = 1)
BEGIN
SET @newResourceXml = CAST('' AS xml);
SET @newResourceXml.modify('insert attribute resource {sql:variable("@waitresource")} into (/unknownlock[1])');
SET @chainXML.modify('insert sql:variable("@newResourceXml") into (//resource-list)[1] ');
END
--find owner
IF NOT (@chainXML.exist('//unknownlock[@resource=sql:variable("@waitresource")]//owner[@id=sql:variable("@blockingid")]') = 1)
BEGIN
-- add owner if needed
SET @newOwnerXml = CAST('' AS xml);
SET @newOwnerXml.modify('insert attribute id {sql:variable("@blockingid")} into (/owner[1])');
SET @chainXML.modify('insert sql:variable("@newOwnerXml") into (//unknownlock[@resource=sql:variable("@waitresource")]/owner-list)[1]');
END
--find waiter
IF NOT (@chainXML.exist('//unknownlock[@resource=sql:variable("@waitresource")]//waiter[@id=sql:variable("@blockedid")]') = 1)
BEGIN
-- add waiter if needed
SET @newWaiterXml = CAST('' AS xml);
SET @newWaiterXml.modify('insert attribute id {sql:variable("@blockedid")} into (/waiter[1])');
SET @chainXML.modify('insert sql:variable("@newWaiterXml") into (//unknownlock[@resource=sql:variable("@waitresource")]/waiter-list)[1]');
END
-- update chain
UPDATE @blockedProcessChains
SET chainxml = @chainXML
WHERE chainid = @chainid
FETCH NEXT FROM bpCursor INTO @bprXML, @chainid
END
CLOSE bpCursor;
DEALLOCATE bpCursor;
-- update list with victim ids
WITH rankedEvents AS
(
SELECT ROW_NUMBER() OVER (PARTITION BY EndTime ORDER BY Duration DESC) AS myRank ,
CAST (TextData AS XML) AS TextData,
EndTime
FROM blocked --hey, remember to save to a trace table in this database named blocked
WHERE EventClass = 137
),
blockers AS
(
SELECT EndTime,
'process' +
re.TextData.value('(//blocking-process/process/@spid)[1]', 'varchar(10)') +
'_' +
re.TextData.value('(//blocking-process/process/@ecid)[1]', 'varchar(10)') AS processId
FROM rankedEvents re
WHERE myRank = 1
)
UPDATE @blockedProcessChains
SET chainxml.modify('insert attribute victim {sql:column("processId")} into (//deadlock)[1]')
FROM @blockedProcessChains bpc
JOIN blockers b ON bpc.ChainId = b.EndTime
SELECT * FROM @blockedProcessChains;