如何使用.nodes()为TSQL中的每个节点(在一个级别上)添加序号?

时间:2016-12-11 18:17:11

标签: xml sql-server-2012

我需要在SQL Server中扩展xml作为表。我使用XQuerynodes()以及.query()来执行此操作。但我需要每个节点都有一个序列号,我还需要根据内部结构过滤节点,我不知道如何做到这一点。

我需要以下结果:

------------------------
| 1  | <xml node ...> |
------------------------
| 2  | <xml node ...> |
------------------------
...

我有以下XML:

<node xmlns="http://mynamespace.com/ns/">
    <subnode xmlns="http://mynamespace.com/ns/">
        <Tag>Domain</Tag>
        <Value>dom</Value>
    </subnode>
    <subnode xmlns="http://mynamespace.com/ns/">
        <Tag>Wdth</Tag>
        <Value>1</Value>
    </subnode>
    <subnode xmlns="http://mynamespace.com/ns/">
        <Tag>Code</Tag>
        <Value>TEST</Value>
    </subnode>
</node>
<node xmlns="http://mynamespace.com/ns/">
    <subnode xmlns="http://mynamespace.com/ns/">
        <Tag>Domain</Tag>
        <Value>dom</Value>
    </subnode>
    <subnode xmlns="http://mynamespace.com/ns/">
        <Tag>Wdth</Tag>
        <Value>1</Value>
    </subnode>
    <subnode xmlns="http://mynamespace.com/ns/">
        <Tag>Code</Tag>
        <Value></Value>
    </subnode>
</node>

<Tag>Code</Tag>的值为空(<Value></Value>)时,我需要跳过此节点。

我使用以下代码,但我无法弄清楚如何制作序列号或如何过滤:

DECLARE @XMLInput XML = '<node xmlns="http://mynamespace.com/ns/">
    <subnode xmlns="http://mynamespace.com/ns/">
        <Tag>Domain</Tag>
        <Value>dom</Value>
    </subnode>
    <subnode xmlns="http://mynamespace.com/ns/">
        <Tag>Wdth</Tag>
        <Value>1</Value>
    </subnode>
    <subnode xmlns="http://mynamespace.com/ns/">
        <Tag>Code</Tag>
        <Value>TEST</Value>
    </subnode>
</node>
<node xmlns="http://mynamespace.com/ns/">
    <subnode xmlns="http://mynamespace.com/ns/">
        <Tag>Domain</Tag>
        <Value>dom</Value>
    </subnode>
    <subnode xmlns="http://mynamespace.com/ns/">
        <Tag>Wdth</Tag>
        <Value>1</Value>
    </subnode>
    <subnode xmlns="http://mynamespace.com/ns/">
        <Tag>Code</Tag>
        <Value></Value>
    </subnode>
</node>';

SELECT 
    Child.query('declare default element namespace "http://mynamespace.com/ns/"; (.)') AS node
FROM
        @XMLInput.nodes('declare default element namespace "http://mynamespace.com/ns/"; (/node)') AS N(Child) 

修改 因为我澄清了不清楚的要素。当存在<node>节点且<subnode>节点的值为<Tag>且对应的"Code"节点为空时,我需要过滤掉整个<Value>节点。在这种情况下,我需要移除整个<node> - 不可见。

1 个答案:

答案 0 :(得分:1)

第一部分相当简单,使用ROW_NUMBER() OVER()。由于XML具有隐式排序顺序,因此我们可以使用(SELECT NULL)。行将根据XML中的物理顺序显示:

使用此代码,您的节点将被编号:

WITH XMLNAMESPACES(DEFAULT 'http://mynamespace.com/ns/')
SELECT ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS NodeNr
      ,Child.query('.') AS node
FROM
    @XMLInput.nodes('/node') AS N(Child) 

你的第二部分对我来说并不清楚。你想完全压制第二个节点,因为有

<subnode xmlns="http://mynamespace.com/ns/">
    <Tag>Code</Tag>
    <Value></Value>
</subnode>

?或者你想要仅仅抑制这个<subnode>

以下内容将使用upper作为派生表CTE),然后在列.exist()上使用node。该方法仅根据XQuery表达式检查任何节点的存在。在这种情况下,我使用<Tag>搜索任何text()="Code"。从那里我们向上导航一级并搜索<Value>为空的text()元素。如果存在,则函数返回1,因此我们需要那些没有:

的函数
WITH XMLNAMESPACES(DEFAULT 'http://mynamespace.com/ns/')
,shredded AS
(
    SELECT ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS NodeNr
            ,Child.query(N'.') AS node
    FROM
        @XMLInput.nodes(N'/node') AS N(Child) 
)
SELECT *
FROM shredded
WHERE shredded.node.exist(N'//Tag[text()="Code"]/../Value[empty(text())]')=0

更新:排序顺序持久性文档

正如@MartinSmith指出的那样,没有证据证明这个

  

行将根据XML中的物理顺序显示

同时我发现了这个:

  • 我在XML in SQL Server 2005找到了一篇论文。在那里你可以找到 XML值以内部格式存储...以支持XML模型特征,例如文档顺序... 这至少是一个提示,即文档的订单是持久的。使用FROM OPENXML读取XML也将返回一个已排序的表(至少我还没有找到任何异常)。

  • 我发现了Understanding the XML data type。您可以在哪里找到此内部表示包括有关包含层次结构,文档顺序以及元素和属性值的信息。具体而言,保留XML数据的InfoSet内容

  • 关于InfoSet I found this document,其中说明了[children]子信息项的有序列表,按文档顺序

嗯,这仍然没有有效的证明,SELECT .nodes()将在所有情况下以与 XML中的完全相同的顺序返回派生表。但是 - 至少 - 它指出了内部秩序值得坚持的事实。

我的结论:内部订单被视为XML文档的固有部分。这就是为什么我很确定,.nodes()将以相同的顺序返回派生表。添加ROW_NUMBER() OVER(ORDER BY (SELECT NULL))除了向这些行添加运行编号之外什么都不做。