SQL Server XQuery - 选择子集

时间:2011-08-08 17:58:04

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

以下面的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功能不允许这样,如果是这样的话就没问题了。但是,如果我能做到这一点,我将非常感谢一个例子。

1 个答案:

答案 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>