SQLXML - 搜索和查询节点元素?

时间:2011-08-02 11:01:04

标签: sql-server sql-server-2005 sql-server-2008 xquery-sql

我有一个存储在XML数据类型列中的XML(在表中会有多个这样的行) -

<Root xmlns="http://tempuri.org" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <Elem1 type="T1">
    <Name type="string" display="First name">John</Name>
    <TimeZone display="Time zone">
      <DisplayName type="string" display="Display name">GMT Standard Time</DisplayName>
    </TimeZone>
  </Elem1>
</Root> 

如何根据节点元素说明(使用SQL SERVER 2008 R2) - 获取所有“Elem1”节点或获取所有“Name”节点或获取所有TimeZone节点?像使用local-name()函数?

编辑 - 部分解决方案 -

我得到了部分解决方案(参见John的回复,然后运行) -

SELECT C1.query('fn:local-name(.)') AS Nodes FROM [dbo].[MyXmlTable] AS MyXML CROSS APPLY MyXML.MyXmlCol.nodes('//*') AS T ( C1 ) 

上面的查询返回TABLE中的所有节点元素。现在,我想对特定元素进行过滤,并返回元素及其值或其属性值。如何实现这一点(通过使用WHERE子句或任何其他过滤机制)?

3 个答案:

答案 0 :(得分:1)

我不确定你要找的是什么结果,但也许这样。

declare @T table(XMLCol xml)
insert into @T values
('<Root xmlns="http://tempuri.org" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <Elem1 type="T1">
    <Name type="string" display="First name">John</Name>
    <TimeZone display="Time zone">
      <DisplayName type="string" display="Display name">GMT Standard Time</DisplayName>
    </TimeZone>
  </Elem1>
</Root>') 

declare @Node varchar(50)
set @Node = 'Elem1'

select N.query('.') as Value
from @T as T
  cross apply T.XMLCol.nodes('//*[local-name()=sql:variable("@Node")]') as X(N)

结果:

<p1:Elem1 xmlns:p1="http://tempuri.org" type="T1">
  <p1:Name type="string" display="First name">John</p1:Name>
  <p1:TimeZone display="Time zone">
    <p1:DisplayName type="string" display="Display name">GMT Standard Time</p1:DisplayName>
  </p1:TimeZone>
</p1:Elem1>

修改

如果你想要实际值而不是整个XML,你可以这样做。

declare @Node varchar(50)
set @Node = 'TimeZone'

select N.value('.', 'varchar(100)') as Value
from @T as T
  cross apply T.XMLCol.nodes('//*[local-name()=sql:variable("@Node")]') as X(N)

结果:

Value
------------------
GMT Standard Time

答案 1 :(得分:0)

我不清楚你的输出应该是什么样子。但是,这应该让你开始:

create table MyXmlTable (MyXmlCol xml)
insert into MyXmlTable (MyXmlCol) values 
(
'
<Root xmlns="http://tempuri.org" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <Elem1 type="T1">
    <Name type="string" display="First name">John</Name>
    <TimeZone display="Time zone">
      <DisplayName type="string" display="Display name">GMT Standard Time</DisplayName>
    </TimeZone>
  </Elem1>
  <Elem1 type="T2">
    <Name type="string" display="First name">Fred</Name>
    <TimeZone display="Time zone">
      <DisplayName type="string" display="Display name">EST Standard Time</DisplayName>
    </TimeZone>
  </Elem1>
</Root> 
');

;WITH XMLNAMESPACES(DEFAULT 'http://tempuri.org')
select MyXmlCol.query('/Root/Elem1/Name')
from MyXmlTable

这会在XML中查询“Name”元素 - 您可以根据所需的输出类型修改查询。它有点长,但关于SQLXML的MSDN文章非常有用:

http://msdn.microsoft.com/en-us/library/ms345117(v=sql.90).aspx

希望这有帮助!

约翰

更新:您可以添加类似这样的where子句。我仍然不清楚你希望输出看起来像什么,但这将过滤掉“Elem1”值:

SELECT C1.query('fn:local-name(.)') AS Nodes 
FROM [dbo].[MyXmlTable] AS MyXML 
CROSS APPLY MyXML.MyXmlCol.nodes('//*') AS T ( C1 ) 
WHERE CAST(C1.query('fn:local-name(.)') AS NVARCHAR(32)) <> 'Elem1'

再一次更新;希望这是你正在寻找的答案!

尝试在查询中使用通配符。我不得不使用动态SQL,因为XML query()函数只会为路径使用字符串文字(您可以使用sql:variable(“@ filter”)作为值,但我无法使其适用于路径。 )

DECLARE @filter nvarchar(20)
SET @filter = '*/Elem1'

DECLARE @sqlCommand nvarchar(1000)
SET @sqlCommand = 
    ';WITH XMLNAMESPACES(DEFAULT ''http://tempuri.org'')
    select MyXmlCol.query(''' + @filter + ''')
    from MyXmlTable'
print @sqlCommand
EXECUTE sp_executesql @sqlCommand, N'@filter nvarchar(20)', @filter = @filter

这将返回Elem1 XML(以及所有子节点):

<p1:Elem1 xmlns:p1="http://tempuri.org" type="T1">
  <p1:Name type="string" display="First name">John</p1:Name>
  <p1:TimeZone display="Time zone">
    <p1:DisplayName type="string" display="Display name">GMT Standard Time</p1:DisplayName>
  </p1:TimeZone>
</p1:Elem1>
<p2:Elem1 xmlns:p2="http://tempuri.org" type="T2">
  <p2:Name type="string" display="First name">Fred</p2:Name>
  <p2:TimeZone display="Time zone">
    <p2:DisplayName type="string" display="Display name">EST Standard Time</p2:DisplayName>
  </p2:TimeZone>
</p2:Elem1>

如果您想挑选“TimeZone”,您可以这样做:

SET @filter = '*/*/TimeZone'

答案 2 :(得分:0)

您可以将XML转换为表格,如下所示:

  declare @XML xml='<Root xmlns="http://tempuri.org" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <Elem1 type="T1">
    <Name type="string" display="First name">John</Name>
    <TimeZone display="Time zone">
      <DisplayName type="string" display="Display name">GMT Standard Time</DisplayName>
    </TimeZone>
  </Elem1>
</Root> '

;WITH XMLNAMESPACES(DEFAULT 'http://tempuri.org'),
numbers as(
SELECT  ROW_NUMBER() OVER(ORDER BY o1.object_id,o2.object_id) Num
FROM    sys.objects o1 CROSS JOIN  sys.objects o2),
 c as(
SELECT 
b.value('local-name(.)','nvarchar(1000)') Node_Name,
b.value('./text()[1]','nvarchar(1000)') Node_Value,
b.value('count(@*)','nvarchar(MAX)') AttributeCount,
Num Attribute_Number
FROM 
@xml.nodes('Root//*') a(b)
CROSS APPLY Numbers
WHERE Num<=b.value('count(@*)','nvarchar(MAX)')
)
SELECT c.Node_Name,c.node_Value,Attribute_Number,
    @XML.query('for $Attr in //*/.[local-name(.)=sql:column("Node_Name")]/@*[sql:column("Attribute_Number")] return local-name($Attr)').value('.','nvarchar(MAX)') Attribute_Name,
    @XML.value('data(//*/.[local-name(.)=sql:column("Node_Name")]/@*[sql:column("Attribute_Number")])[1]','nvarchar(1000)') Attribute_Value
FROM    c

<强>结果:

Node_Name   node_Value          Attribute_Number    Attribute_Name  Attribute_Value
Elem1        NULL                       1              type             T1
Name         John                       1              type           string
Name         John                       2              display        First name
TimeZone     NULL                       1              display        Time zone
DisplayName GMT Standard Time           1              type            string
DisplayName GMT Standard Time           2              display     Display name

稍后您可以查询此结果以获取您需要的节点/属性值。

但它仅适用于您的示例,当您只有一个节点且所有名称都是唯一的时。在多节点XML中,您应该使用像“1-1-2”这样的分层编号。它要复杂得多,我不建议这样做。