如何从SQL Server XML死锁图中提取SQL

时间:2018-03-16 21:06:47

标签: sql-server powershell xml-parsing

我有一些SQL死锁,我试图捕获mediaName。死锁报告是XML格式,但我需要的属性隐藏在XML中,然后是SQL,然后是XML。这是一个例子。

SQL启动的位置为/deadlock/process-list/process/inputbuf,然后SQL为:

SET DEADLOCK_PRIORITY 8; 
EXEC spM_Ext_InsertUpdateXML N'<mediaRecords><media 
title="This Is the title" mediaType="0" 
creationTime="2018-03-16T00:59:43" origSOM="01:00:00;00" notes="Air Date: 
2018-03-18 
Air Window: 3 
" mediaName="This is what i need" 
><mediaInstances><mediaInstance directory="here" 
duration="00:28:40;11" version="1" position="00:00:00;00" mediaSetId="34" 
creationStartTime="2018-03-16T00:59:43;25" creationEndTime="2018-03-
16T00:59:43;25"/></mediaInstances><properties>< 
classifications><classification category="HD" classification="Content 
Resolution"/></classifications><markups><markup 
name=""><Item duration="00:00:10;00" orderNo="1" 
type="Dynamic" som="00:59:50;00" comment="" 
name="Segment"/></markup><markup 
name="Segment"><markupItem duration="00:08:41;10" orderNo="2" 
type="Dynamic" som="01:00:00;00" comment="Main Title and Segment 1 | 
ID:SEDC" name="Segment"/></markup><markup 
name="Black"><markup

了解XML如何使用<>来表示元素,而&lt&gt则增加了复杂性。

我正在尝试从此报告中仅提取mediaName,但无法通过PowerShell获取上述XPath。希望有人可能会有一个想法。我正在使用

$xml = [xml](Get-Content "C:\Users\user\desktop\test.xml")

$xml.SelectNodes('/deadlock/process-list/process/inputbuf')  | select mediaName

我还尝试过select-xml where-object,但我认为我没有使用正确的$_.[input]

在tomalak和下面的答案的帮助下,这是固定和工作的解析脚本。

#report file location, edited by user when needed
$DeadlockReport =  "C:\Users\User\Desktop\xml_report1.xml"

# Create object to load the XML from the deadlock report and find the SQL within
$xml = New-Object xml 
$xml.Load($DeadlockReport) 
$inputbuf = $xml.SelectNodes('//deadlock/process-list/process/inputbuf')
$value = $inputbuf.'#text' 

#find the internal XML and replace bad values, SQL, and truncation with RE
$value = $value -replace "^[\s\S]*?N'","" -replace "';\s*$","" -replace "<markup.*$","</properties></media></mediaRecords>" 

#append root elements to $value 
$fix = "<root>" + $value + "</root>"

#Load the XML after its been corrected
$payload.LoadXml($fix)

#find the nodes in the xml for mediaName
$mediaName = $payload.SelectNodes('//root/mediaRecords/media/@mediaName') 

#iterate through and return all media names. 
foreach($i in $mediaName)
    { 
return $mediaName 
    }

1 个答案:

答案 0 :(得分:0)

你拥有的是:

  • 一个XML文件,
  • ,其中包含字符串值
  • 是SQL,
  • 其中包含另一个字符串值
  • 再次是XML。

让我们剥洋葱。

首先,请不要加载像这样的XML文件

# this is bad code, don't use
$xml = [xml](Get-Content "C:\Users\user\desktop\test.xml")

XML具有复杂的文件编码检测功能,您可以通过让Powershell加载文件来使其短路。这可能导致数据无声地中断,因为Powershell的Get-Content不知道XML文件的实际编码是什么。 (有时上述作品,有时它不会。&#34;它适用于我&#34;并不意味着你做得对,这意味着你的存在幸运的。)

这是正确的方法:

$xml = New-Object xml
$xml.Load("C:\Users\user\desktop\test.xml")

这里XmlDocument object将负责加载文件并透明地适应它可能具有的任何编码。没有什么可以打破,你不必担心文件编码。

其次,不要让XML文件在文本编辑器中的外观欺骗你。如上所述,/deadlock/process-list/process/inputbuf包含字符串,就XML而言,<>,当您查看实际情况时,所有其他内容都会出现在那里元素的文本值。

$inputbuf = $xml.SelectSingleNode('/deadlock/process-list/process/inputbuf')
$value = $inputbuf.'#text'

Write-Host $value

会打印这样的东西,即SQL:

SET DEADLOCK_PRIORITY 8; 
EXEC spM_Ext_InsertUpdateXML N'<mediaRecords><media 
title="This Is the title" mediaType="0" 
creationTime="2018-03-16T00:59:43" origSOM="01:00:00;00" notes="Air Date: 
2018-03-18 &#xa;Air Window: 3 &#xa;" mediaName="This is what i need" 
><mediaInstances><mediaInstance directory="here" 
duration="00:28:40;11" version="1" position="00:00:00;00" mediaSetId="34" 
creationStartTime="2018-03-16T00:59:43;25" creationEndTime="2018-03-
16T00:59:43;25"/></mediaInstances><properties>< 
classifications><classification category="HD" classification="Content 
Resolution"/></classifications><markups><markup 
name=""><Item duration="00:00:10;00" orderNo="1" 
type="Dynamic" som="00:59:50;00" comment="" 
name="Segment"/></markup><markup 
name="Segment"><markupItem duration="00:08:41;10" orderNo="2" 
type="Dynamic" som="01:00:00;00" comment="Main Title and Segment 1 | 
ID:SEDC" name="Segment"/></markup><markup 
name="Black"><markup ...
</mediaRecords>';

您感兴趣的XML实际上是此SQL中的字符串。如果SQL遵循这种模式......

SET DEADLOCK_PRIORITY 8; 
EXEC spM_Ext_InsertUpdateXML N'<...>';

...为了获得XML有效负载,我们需要做三件事:

  1. 删除附带的SQL语句
  2. 将所有''替换为'(因为''是SQL字符串中的转义引号)
  3. 祈祷中间的部分不包含任何其他SQL表达式
  4. 所以

    $value = $value -replace "^[\s\S]*?N'","" -replace "';\s*$","" -replace "''","'"
    

    会删除包括N'';在内的所有内容,并将所有重复的单引号(如果有)替换为普通单引号。

    根据需要调整正则表达式。用regex替换SQL部分并不完全干净,但是如果预期的输入非常有限,就像在这种情况下一样,它就会这样做。

    Write-Host $value
    

    现在我们应该有一个实际 XML的字符串。让我们解析它。这一次,它已经在我们的记忆中,没有任何文件编码需要注意。因此,如果我们直接将其转换为XML,它实际上是正确的:

    $payload = [xml]$value
    

    现在我们可以查询您感兴趣的值:

    $mediaName = $payload.SelectSingleNode("/mediaRecords/media/@mediaName")
    
    Write-Host $mediaName