【问题标题】:Replace XML node element with a variable用变量替换 XML 节点元素
【发布时间】:2016-12-23 20:31:25
【问题描述】:

我目前有以下代码在名为 PARTY 的父节点下创建 XML。在 PARTY 节点内部是另一个称为 ROLE 的节点。我的问题是如何用 xquery 中的 [SequenceNr] 变量替换当前的 ROLE 节点,它只是说 ROLE。

我曾尝试在 @OutputXml 中进行替换,但没有奏效,我认为在 xquery 中应该有一个我不知道的简单方法。

当前代码:

WITH [PartyNodes] AS
        (
            SELECT  (   
                        SELECT  [dbo].[XmlA](@Id),
                                [dbo].[XmlB](@Id)
                        FOR XML PATH(''),TYPE
                    ) AS [AllTogether]
        )
        ,[NumberedSequences] AS
        (
            SELECT  'PARTY' + CONVERT(NVARCHAR, ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) + 10) + '_ROLE1' AS [PartySequenceNr], -- party label
                    ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) + 10 AS [SequenceNr], -- party sequence
                    [T].[Party].[query]('.') AS [TheNode]
            FROM [PartyNodes]
            CROSS APPLY [AllTogether].[nodes]('/PARTY') AS T(Party)
        )
        SELECT @OutputXml = (   SELECT [TheNode].[query]('let $p:=/PARTY[1]
                                                      let $lbl:=sql:column("PartySequenceNr")
                                                      let $nr:=sql:column("SequenceNr")
                                                      return
                                                     <PARTY SequenceNumber="{$nr}" xlink_label="{$lbl}" >
                                                     {$p/*}
                                                    </PARTY>'
                                                )
                                FROM [NumberedSequences]
                                FOR XML PATH(''),ROOT('PARTIES')
                                );

当前输出:

<PARTY SequenceNumber="1" xlink:label="PARTY1_ROLE1">
    <INDIVIDUAL>
      <NAME>
         <FullName>Test</FullName>
       </NAME>
    </INDIVIDUAL>
    <ROLES>
       <ROLE>
            <A>Test</A>
       </ROLE>
    </ROLES>
</PARTY>

期望的输出:

<PARTY SequenceNumber="1" xlink:label="PARTY1_ROLE1">
    <INDIVIDUAL>
      <NAME>
         <FullName>Test</FullName>
       </NAME>
    </INDIVIDUAL>
    <ROLES>
       <ROLE SequenceNumber="1" xlink:label="PARTY1_ROLE1">>
            <A>Test</A>
       </ROLE>
    </ROLES>
</PARTY>

【问题讨论】:

    标签: sql-server xml sql-server-2008 xquery


    【解决方案1】:

    我试图跟随你的想法,但无法...

    有两个函数XmlAXmlB。首先,您将他们的结果合并到AllTogether。稍后您使用.nodes('/PARTY') 从中读取,但您显示的XML 只有一个PARTY 节点作为根。

    您还可以使用FLWOR-XQuery插入您的两个属性。但我怀疑xlink_label 是否会显示为xlink:label

    您显示的 XML 不能是上述结果...或者我遗漏了什么...

    我的第一个想法是:这似乎过于复杂......如果以下内容没有帮助,请提供有关您的目标的更多详细信息,请尝试set up a MCVE。这可能是the XY-problem

    我从没有属性的 XML 开始,假设这是前面的结果:

    DECLARE @xml XML=
    N'<PARTY>
        <INDIVIDUAL>
          <NAME>
             <FullName>Test</FullName>
           </NAME>
        </INDIVIDUAL>
        <ROLES>
           <ROLE>
                <A>Test</A>
           </ROLE>
        </ROLES>
    </PARTY>';
    

    --此查询将在PARTYROLE 中插入属性

    DECLARE @PartySequenceNr INT = 1;
    DECLARE @SequenceNr VARCHAR(100)='PARTY1_ROLE1';
    
    SELECT @xml.query
    (N' 
        let $p:=/PARTY[1]
        let $lbl:=sql:variable("@PartySequenceNr")
        let $nr:=sql:variable("@SequenceNr")
        return
        <PARTY SequenceNumber="{$nr}" xlink_label="{$lbl}" >
        {
            for $nd in $p/*
            return
                if(local-name($nd)="ROLES") then
                ( 
                    <ROLES><ROLE SequenceNumber="{$nr}" xlink_label="{$lbl}" >
                    {$nd/ROLE/*}
                    </ROLE></ROLES>
                )
                else $nd
        }
        </PARTY>
    ') 
    

    结果:

    <PARTY SequenceNumber="PARTY1_ROLE1" xlink_label="1">
      <INDIVIDUAL>
        <NAME>
          <FullName>Test</FullName>
        </NAME>
      </INDIVIDUAL>
      <ROLES>
        <ROLE SequenceNumber="PARTY1_ROLE1" xlink_label="1">
          <A>Test</A>
        </ROLE>
      </ROLES>
    </PARTY>
    

    使用命名空间xlink

    如果您需要命名空间,请使用它

    SELECT @xml.query
    (N' declare namespace xlink="DummyUrl";
        let $p:=/PARTY[1]
        let $lbl:=sql:variable("@PartySequenceNr")
        let $nr:=sql:variable("@SequenceNr")
        return
        <PARTY SequenceNumber="{$nr}" xlink:label="{$lbl}" >
        {
        for $nd in $p/*
        return
            if(local-name($nd)="ROLES") then
            ( 
                <ROLES><ROLE SequenceNumber="{$nr}" xlink:label="{$lbl}" >
    
                {$nd/ROLE/*}
    
                </ROLE></ROLES>
            )
            else $nd
        }
        </PARTY>
    ')
    

    结果

    <PARTY SequenceNumber="PARTY1_ROLE1" xmlns:xlink="DummyUrl" xlink:label="1">
      <INDIVIDUAL>
        <NAME>
          <FullName>Test</FullName>
        </NAME>
      </INDIVIDUAL>
      <ROLES>
        <ROLE SequenceNumber="PARTY1_ROLE1" xlink:label="1">
          <A>Test</A>
        </ROLE>
      </ROLES>
    </PARTY>
    

    【讨论】:

    • 哇,太棒了。这效率如何?或者我将如何测试它的性能。实际上,我最终以不同的方式做到了这一点。我创建了一个表格类型,在其中插入了我的标签和一些其他信息,然后我将该变量类型传递给我的派对函数并从那里选择特定记录。
    • @DRockClimber,您基本上可以使用DECLARE @d DATETIME=GETDATE(); DoSomething; SELECT CAST(GETDATE()-@d AS TIME); 来测试性能但是这种方法 - 可以肯定 - 不是最好的......创建XML只是为了通过一些FLWOR-XQuery来按下它一定很慢...你的整个事情闻起来很复杂,但我不知道任何细节。正如所指出的,即使提供的信息也不一致......
    • 你能帮我澄清一下吗?你在哪里指定 $nd 在 if 条件中检查它之前保存 ROLES 的值?
    • @DRockClimber 这一行负责$nd的值:for $nd in $p/*这将从$p读取所有子节点。处理for-loop 会将每个子节点一个接一个地移动到$nd。节点 ROLES 需要不同的处理方式,因此 if 使用 local-name() 检查。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-08-27
    • 2015-07-16
    • 2022-12-01
    • 1970-01-01
    • 2020-01-18
    • 2018-03-05
    相关资源
    最近更新 更多