我有一个存储在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子句或任何其他过滤机制)?
答案 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”这样的分层编号。它要复杂得多,我不建议这样做。