SQL Server XQuery - 选择子集

发布于 2024-11-28 22:13:23 字数 1276 浏览 0 评论 0原文

以下面的 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 功能不允许这样做,如果是这样也没关系。然而,如果我能做到这一点,我将非常感激一个例子。

Take for example the following XML:

Initial Data

<computer_book>
    <title>Selecting XML Nodes the Fun and Easy Way</title>
    <isbn>9999999999999</isbn>
    <pages>500</pages>
    <backing>paperback</backing>
</computer_book>

and:

<cooking_book>
    <title>50 Quick and Easy XML Dishes</title>
    <isbn>5555555555555</isbn>
    <pages>275</pages>
    <backing>paperback</backing>
</cooking_book>

I have something similar in a single xml-typed column of a SQL Server 2008 database. Using SQL Server XQuery, would it be possible to get results such as this:

Resulting Data

<computer_book>
    <title>Selecting XML Nodes the Fun and Easy Way</title>
    <pages>500</pages>
</computer_book>

and:

<cooking_book>
    <title>50 Quick and Easy XML Dishes</title>
    <isbn>5555555555555</isbn>
</cooking_book>

Please note that I am not referring to selecting both examples in one query; rather I am selecting each via its primary key (which is in another column). In each case, I am essentially trying to select the root and an arbitrary subset of children. The roots can be different, as seen above, so I do not believe I can hard-code the root node name into a "for xml" clause.

I have a feeling SQL Server's XQuery capabilities will not allow this, and that is fine if it is the case. If I can accomplish this, however, I would greatly appreciate an example.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

如若梦似彩虹 2024-12-05 22:13:23

这是我在下面的查询中使用的测试数据:

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>

Here is the test data I used in the queries below:

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>')

You can filter the nodes under to root node like this using local-name() and a list of the node names you want:

select XMLCol.query('/*/*[local-name()=("isbn","pages")]')
from @T

Result:

<isbn>9999999999999</isbn><pages>500</pages>
<isbn>5555555555555</isbn><pages>275</pages>

If I understand you correctly the problem with this is that you don't get the root node back.

This query will give you an empty root node:

select cast('<'+XMLCol.value('local-name(/*[1])', 'varchar(100)')+'/>' as xml)
from @T

Result:

<computer_book />
<cooking_book />

From this I have found two solutions for you.

Solution 1

Get the nodes from your table to a table variable and then modify the XML to look like you want.

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

Result:

RootNode
<computer_book><isbn>9999999999999</isbn><pages>500</pages></computer_book>
<cooking_book><isbn>5555555555555</isbn><pages>275</pages></cooking_book>

The sad part with this solution is that it does not work with SQL Server 2005.

Solution 2

Get the parts, build the XML as a string and cast it back to 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

Result:

<computer_book><isbn>9999999999999</isbn><pages>500</pages></computer_book>
<cooking_book><isbn>5555555555555</isbn><pages>275</pages></cooking_book>

Making the nodes parameterized

In the queries above the nodes you get as child nodes is hard coded in the query. You can use sql:varaible() to do this instead. I have not found a way of making the number of nodes dynamic but you can add as many as you think you need and have null as value for the nodes you don't need.

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

Result:

<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>
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文