以下面的XML为例:
初始数据
<computer_book>
<title>Selecting XML Nodes the Fun and Easy Way</title>
<isbn>9999999999999</isbn>
<pages>500</pages>
<backing>paperback</backing>
</computer_book>
和
<cooking_book>
<title>50 Quick and Easy XML Dishes</title>
<isbn>5555555555555</isbn>
<pages>275</pages>
<backing>paperback</backing>
</cooking_book>
我在SQL Server 2008数据库的单个xml类型列中有类似的东西。使用SQL Server XQuery,是否可以获得如下结果:
产生的数据
<computer_book>
<title>Selecting XML Nodes the Fun and Easy Way</title>
<pages>500</pages>
</computer_book>
和
<cooking_book>
<title>50 Quick and Easy XML Dishes</title>
<isbn>5555555555555</isbn>
</cooking_book>
请注意,我不是指在一个查询中选择两个示例;而是我通过其主键(在另一列中)选择每个。在每种情况下,我基本上都试图选择根和子集的任意子集。如上所示,根可以是不同的,所以我不相信我可以将根节点名称硬编码为“for xml”子句。
我有一种感觉SQL Server的XQuery功能不允许这样,如果是这样的话就没问题了。但是,如果我能做到这一点,我将非常感谢一个例子。
答案 0 :(得分:3)
以下是我在下面的查询中使用的测试数据:
declare @T table (XMLCol xml)
insert into @T values
('<computer_book>
<title>Selecting XML Nodes the Fun and Easy Way</title>
<isbn>9999999999999</isbn>
<pages>500</pages>
<backing>paperback</backing>
</computer_book>'),
('<cooking_book>
<title>50 Quick and Easy XML Dishes</title>
<isbn>5555555555555</isbn>
<pages>275</pages>
<backing>paperback</backing>
</cooking_book>')
您可以使用local-name()和所需节点名称列表来过滤根节点下的节点:
select XMLCol.query('/*/*[local-name()=("isbn","pages")]')
from @T
结果:
<isbn>9999999999999</isbn><pages>500</pages>
<isbn>5555555555555</isbn><pages>275</pages>
如果我理解正确,那么问题就是你没有恢复根节点。
此查询将为您提供一个空的根节点:
select cast('<'+XMLCol.value('local-name(/*[1])', 'varchar(100)')+'/>' as xml)
from @T
结果:
<computer_book />
<cooking_book />
由此我找到了两种解决方案。
解决方案1
将表中的节点获取到表变量,然后将XML修改为您想要的样式。
-- Table variable to hold the node(s) you want
declare @T2 table (RootNode xml, ChildNodes xml)
-- Fetch the xml from your table
insert into @T2
select cast('<'+XMLCol.value('local-name(/*[1])', 'varchar(100)')+'/>' as xml),
XMLCol.query('/*/*[local-name()=("isbn","pages")]')
from @T
-- Add the child nodes to the root node
update @T2 set
RootNode.modify('insert sql:column("ChildNodes") into (/*)[1]')
-- Fetch the modified XML
select RootNode
from @T2
结果:
RootNode
<computer_book><isbn>9999999999999</isbn><pages>500</pages></computer_book>
<cooking_book><isbn>5555555555555</isbn><pages>275</pages></cooking_book>
此解决方案令人遗憾的是它无法与SQL Server 2005一起使用。
解决方案2
获取部件,将XML构建为字符串并将其转换回XML。
select cast('<'+XMLCol.value('local-name(/*[1])', 'varchar(100)')+'>'+
cast(XMLCol.query('/*/*[local-name()=("isbn","pages")]') as varchar(max))+
'</'+XMLCol.value('local-name(/*[1])', 'varchar(100)')+'>' as xml)
from @T
结果:
<computer_book><isbn>9999999999999</isbn><pages>500</pages></computer_book>
<cooking_book><isbn>5555555555555</isbn><pages>275</pages></cooking_book>
使节点参数化
在上面的查询中,您作为子节点获得的节点在查询中是硬编码的。您可以使用sql:varaible()
来执行此操作。我还没有找到使节点数量动态化的方法,但您可以根据需要添加任意数量的节点,并将null
作为您不需要的节点的值。
declare @N1 varchar(10)
declare @N2 varchar(10)
declare @N3 varchar(10)
declare @N4 varchar(10)
set @N1 = 'isbn'
set @N2 = 'pages'
set @N3 = 'backing'
set @N4 = null
select cast('<'+XMLCol.value('local-name(/*[1])', 'varchar(100)')+'>'+
cast(XMLCol.query('/*/*[local-name()=(sql:variable("@N1"),
sql:variable("@N2"),
sql:variable("@N3"),
sql:variable("@N4"))]') as varchar(max))+
'</'+XMLCol.value('local-name(/*[1])', 'varchar(100)')+'>' as xml)
from @T
结果:
<computer_book><isbn>9999999999999</isbn><pages>500</pages><backing>paperback</backing></computer_book>
<cooking_book><isbn>5555555555555</isbn><pages>275</pages><backing>paperback</backing></cooking_book>