移动 XML 中的节点并使用 XML DML 重命名它们

Moving nodes in XML and rename them using XML DML

鉴于以下 XML 所有名为 IsEurozone 的节点必须重命名为 HasFxrEuro.

<?xml version="1.0" encoding="utf-8"?>
<MacroScenarios>
  <MacroScenario>
    <Id>1</Id>
    <Name>Macro Scenario 1</Name>
    <Scenarios>
      <Scenario>
        <CountryIsoAlpha3>USA</CountryIsoAlpha3>
        <Id>1</Id>
        <IsEurozone>true</IsEurozone>
      </Scenario>
      <Scenario>
        <CountryIsoAlpha3>GER</CountryIsoAlpha3>
        <Id>2</Id>
        <IsEurozone>true</IsEurozone>
      </Scenario>
    </Scenarios>
  </MacroScenario>
</MacroScenarios>

替换操作很容易完成(感谢 Mikael Erikssons post How to rename XML node name in a SQL Server):

UPDATE [dbo].[MacroScenarioSets] set
[ContentAsXml] = 
        replace(
            replace(
                cast(ContentAsXml as nvarchar(max)), 
                '<IsEurozone>', 
                '<HasFxrEuro>'),
            '</IsEurozone>', 
            '</HasFxrEuro>')

之后场景节点的内容将如下所示:

  <Scenario>
    <CountryIsoAlpha3>USA</CountryIsoAlpha3>
    <Id>1</Id>
    <HasFxrEuro>true</HasFxrEuro>
  </Scenario>

但在我的例子中,我需要节点的严格字母顺序,因此 HasFxrEuro 必须放在 Id.[=17 之前=]

我的想法是做类似

的事情
UPDATE [dbo].[MacroScenarioSets]
SET ContentAsXml.modify('insert 
<HasFxrEuro>{(/MacroScenarios/MacroScenario/Scenarios/Scenario/IsEurozone/text())}</HasFxrEuro>
after (/MacroScenarios/MacroScenario/Scenarios/Scenario/CountryIsoAlpha3)')

然后删除所有旧的 IsEurozone 节点。但是 XML DML 需要一个用于插入语句的单例。是否有另一种方法可以使用 XML DML 来实现?

好吧,通过手动浏览 XML 文档,我终于明白了 运行:

    declare @scenarioCursor int = 1
    declare @macroScenarioCursor int = 1

    while 1 = 1
    begin

            -- Add HasFxrEuro with the value of IsEurozone
           UPDATE @myxml
           SET content.modify('
               insert 
                <HasFxrEuro>{(/MacroScenarios/MacroScenario[sql:variable("@macroScenarioCursor")]/Scenarios/Scenario[sql:variable("@scenarioCursor")]/IsEurozone[1]/text())}</HasFxrEuro>
               after ((/MacroScenarios/MacroScenario[sql:variable("@macroScenarioCursor")]/Scenarios/Scenario[sql:variable("@scenarioCursor")]/CountryIsoAlpha3))[1]')
           WHERE content.exist('
                (/MacroScenarioSet/MacroScenarios/MacroScenario[sql:variable("@macroScenarioCursor")]/Scenarios/Scenario[sql:variable("@scenarioCursor")])') = 1

           if @@ROWCOUNT = 0 
           begin
                -- the end
                if @scenarioCursor = 1
                    break;
                else
                    -- increment cursor over MacroScenario, reset cursor over Scenario
                   begin
                         SET @macroScenarioCursor = @macroScenarioCursor + 1
                         SET @scenarioCursor = 1
                   end 
            end       
            else
                begin

                    -- HasFxrEuro has been added remove IsEurozone
                    UPDATE @myxml
                    SET content.modify('
                        delete (/MacroScenarios/MacroScenario[sql:variable("@macroScenarioCursor")]/Scenarios/Scenario[sql:variable("@scenarioCursor")]/IsEurozone)')

                    -- increment Scenario cursor
                    SET @scenarioCursor = @scenarioCursor + 1
               end

end

求解速度很慢。但我不知道如何在 SQL.

中解决这个问题