如何从SQL Server中的表查询Xml值和属性?

我有一个包含一个Xml列的表:

 SELECT * FROM Sqm 

在这里输入图像说明

一行的xml数据的示例是:

 <Sqm version="1.2"> <Metrics> <Metric id="TransactionCleanupThread.RecordUsedTransactionShift" type="timer" unit="µs" count="1" sum="21490" average="21490" minValue="73701" maxValue="73701" >73701</Metric> <Metric id="TransactionCleanupThread.RefundOldTrans" type="timer" unit="µs" count="1" sum="184487" average="184487" minValue="632704" maxValue="632704" >632704</Metric> <Metric id="Database.CreateConnection_SaveContextUserGUID" type="timer" unit="µs" count="2" sum="7562" average="3781" minValue="12928" maxValue="13006" standardDeviation="16" >12967</Metric> <Metric id="Global.CurrentUser" type="timer" unit="µs" count="6" sum="4022464" average="670411" minValue="15" maxValue="13794345" standardDeviation="1642047">2299194</Metric> <Metric id="Global.CurrentUser_FetchIdentityFromDatabase" type="timer" unit="µs" count="1" sum="4010057" average="4010057" minValue="13752614" maxValue="13752614" >13752614</Metric> </Metrics> </Sqm> 

在这个数据的情况下,我想要:

 SqmId id type unit count sum minValue maxValue standardDeviation Value ===== =================================================== ===== ==== ===== ====== ======== ======== ================= ====== 1 TransactionCleanupThread.RecordUsedTransactionShift timer µs 1 21490 73701 73701 NULL 73701 1 TransactionCleanupThread.RefundOldTrans timer µs 1 184487 632704 632704 NULL 632704 1 Database.CreateConnection_SaveContextUserGUID timer µs 2 7562 12928 13006 16 12967 1 Global.CurrentUser timer µs 6 4022464 15 13794345 1642047 2299194 1 Global.CurrentUser_FetchIdentityFromDatabase timer µs 1 4010057 13752614 13752614 NULL 13752614 2 ... 

最后,我将实际执行SUM()MIN()MAX()聚合。 但现在我只是想查询一个XML列。

在伪代码中,我会尝试如下所示:

 SELECT SqmId, Data.query('/Sqm/Metrics/Metric/@id') AS id, Data.query('/Sqm/Metrics/Metric/@type') AS type, Data.query('/Sqm/Metrics/Metric/@unit') AS unit, Data.query('/Sqm/Metrics/Metric/@sum') AS sum, Data.query('/Sqm/Metrics/Metric/@count') AS count, Data.query('/Sqm/Metrics/Metric/@minValue') AS minValue, Data.query('/Sqm/Metrics/Metric/@maxValue') AS maxValue, Data.query('/Sqm/Metrics/Metric/@standardDeviation') AS standardDeviation, Data.query('/Sqm/Metrics/Metric') AS value FROM Sqm 

但是,该SQL查询不起作用:

Msg 2396,Level 16,State 1,Line 2
XQuery [Sqm.data.query()]:属性可能不会出现在元素之外

我已经find了,而且Xml查询是如何logging不完善或者是exampled是多么令人吃惊。 大多数资源而不是查询 ,查询variables ; 我没有做。 大多数资源只使用xml查询进行筛选和select,而不是读取值。 大多数资源读取硬编码子节点(按索引),而不是实际值。

我读过的相关资源

  • https://stackoverflow.com/questions/966441/xml-query-in-sql-server-2008
  • SQL Server查询元素值的xml属性
  • SQL查询XML属性
  • SQL Server 2005 XQuery和XML-DML – 第1部分
  • BOL:Microsoft SQL Server 2005中的XML支持
  • 在SQL Server中查询XML
  • 基本的SQL Server XML查询
  • BOL:query()方法(xml数据types)
  • XML Workshop V – 从XML列读取值
  • SQL SERVER – 发现XML数据types方法介绍 – 入门

更新:.value而不是.query

我试着随机使用.value代替.query

 SELECT Sqm.SqmId, Data.value('/Sqm/Metrics/Metric/@id', 'varchar(max)') AS id, Data.value('/Sqm/Metrics/Metric/@type', 'varchar(max)') AS type, Data.value('/Sqm/Metrics/Metric/@unit', 'varchar(max)') AS unit, Data.value('/Sqm/Metrics/Metric/@sum', 'varchar(max)') AS sum, Data.value('/Sqm/Metrics/Metric/@count', 'varchar(max)') AS count, Data.value('/Sqm/Metrics/Metric/@minValue', 'varchar(max)') AS minValue, Data.value('/Sqm/Metrics/Metric/@maxValue', 'varchar(max)') AS maxValue, Data.value('/Sqm/Metrics/Metric/@standardDeviation', 'varchar(max)') AS standardDeviation, Data.value('/Sqm/Metrics/Metric', 'varchar(max)') AS value FROM Sqm 

但是这也不起作用:

消息2389,级别16,状态1,行3 XQuery [Sqm.data.value()]:
'value()'需要一个单例(或空序列),find的types为'xdt:untypedAtomic *'的操作数

其实你接近你的目标,你只需要使用nodes()方法拆分你的行,然后得到值:

 select s.SqmId, mcvalue('@id', 'varchar(max)') as id, mcvalue('@type', 'varchar(max)') as type, mcvalue('@unit', 'varchar(max)') as unit, mcvalue('@sum', 'varchar(max)') as [sum], mcvalue('@count', 'varchar(max)') as [count], mcvalue('@minValue', 'varchar(max)') as minValue, mcvalue('@maxValue', 'varchar(max)') as maxValue, mcvalue('.', 'nvarchar(max)') as Value, mcvalue('(text())[1]', 'nvarchar(max)') as Value2 from sqm as s outer apply s.data.nodes('Sqm/Metrics/Metric') as m(c) 

sql小提琴演示

使用value而不是query (必须指定要在XQuery中返回的节点的索引以及传递sql数据types作为第二个参数返回):

 select xt.Id , xmvalue( '@id[1]', 'varchar(max)' ) MetricId from XmlTest xt cross apply xt.XmlData.nodes( '/Sqm/Metrics/Metric' ) x(m) 

我一直在试图做一些非常相似的事情,但不使用节点。 但是,我的XML结构有点不同。

你有这样的:

 <Metrics> <Metric id="TransactionCleanupThread.RefundOldTrans" type="timer" ...> 

如果它是这样的,而不是:

 <Metrics> <Metric> <id>TransactionCleanupThread.RefundOldTrans</id> <type>timer</type> . . . 

那么你可以简单地使用这个SQL语句。

 SELECT Sqm.SqmId, Data.value('/Sqm/Metrics/Metric/id)[1]', 'varchar(max)') as id, Data.value('/Sqm/Metrics/Metric/type)[1]', 'varchar(max)') AS type, Data.value('/Sqm/Metrics/Metric/unit)[1]', 'varchar(max)') AS unit, Data.value('/Sqm/Metrics/Metric/sum)[1]', 'varchar(max)') AS sum, Data.value('/Sqm/Metrics/Metric/count)[1]', 'varchar(max)') AS count, Data.value('/Sqm/Metrics/Metric/minValue)[1]', 'varchar(max)') AS minValue, Data.value('/Sqm/Metrics/Metric/maxValue)[1]', 'varchar(max)') AS maxValue, Data.value('/Sqm/Metrics/Metric/stdDeviation)[1]', 'varchar(max)') AS stdDeviation, FROM Sqm 

对我来说,这比使用外部应用程序或交叉应用程序更容易混淆。

我希望这可以帮助别人寻找更简单的解决scheme!

我不明白为什么有些人build议使用cross applyouter apply将XML转换成值的表。 对我来说,这只是带来了太多的数据。

这是我如何创build一个xml对象,然后把它变成一个表的例子。

(我已经在我的xmlstring中添加了空格,只是为了方便阅读。)

 DECLARE @str nvarchar(2000) SET @str = '' SET @str = @str + '<users>' SET @str = @str + ' <user>' SET @str = @str + ' <firstName>Mike</firstName>' SET @str = @str + ' <lastName>Gledhill</lastName>' SET @str = @str + ' <age>31</age>' SET @str = @str + ' </user>' SET @str = @str + ' <user>' SET @str = @str + ' <firstName>Mark</firstName>' SET @str = @str + ' <lastName>Stevens</lastName>' SET @str = @str + ' <age>42</age>' SET @str = @str + ' </user>' SET @str = @str + ' <user>' SET @str = @str + ' <firstName>Sarah</firstName>' SET @str = @str + ' <lastName>Brown</lastName>' SET @str = @str + ' <age>23</age>' SET @str = @str + ' </user>' SET @str = @str + '</users>' DECLARE @xml xml SELECT @xml = CAST(CAST(@str AS VARBINARY(MAX)) AS XML) -- Iterate through each of the "users\user" records in our XML SELECT x.Rec.query('./firstName').value('.', 'nvarchar(2000)') AS 'FirstName', x.Rec.query('./lastName').value('.', 'nvarchar(2000)') AS 'LastName', x.Rec.query('./age').value('.', 'int') AS 'Age' FROM @xml.nodes('/users/user') as x(Rec) 

这里是输出:

在这里输入图像说明