3

给定一个包含以下内容的 XML 变量:

<Root>
    <Parent>
        <ItemID>28</ItemID>
        <Child>
            <ItemID>28</ItemID>
        </Child>
        <Child>
            <ItemID>28</ItemID>
        </Child>
    </Parent>
    <Parent>
        <ItemID>38</ItemID>
        <Child>
            <ItemID>38</ItemID>
        </Child>
    </Parent>
</Root>

我需要将每个 /Root/Parent/ItemID 值替换为从 -1 开始的递减数字,然后将每个 Child/ItemID 替换为其父 ItemID 元素中的值。例如,上面的 XML 应该被转换成这样:

<Root>
    <Parent>
        <ItemID>-1</ItemID>
        <Child>
            <ItemID>-1</ItemID>
        </Child>
        <Child>
            <ItemID>-1</ItemID>
        </Child>
    </Parent>
    <Parent>
        <ItemID>-2</ItemID>
        <Child>
            <ItemID>-2</ItemID>
        </Child>
    </Parent>
</Root>

在 c# 中很容易做到,我很难在 SQL Server 中获得正确的语法。不幸的是,我必须这样做才能使用遗留系统。

4

2 回答 2

3

您的 XML 无效,因此我将其更改为我认为您的意思。

您可以粉碎 XML,使用 row_number() 计算新的 ItemID 并使用for xml path.

declare @XML xml = '
<Root>
    <Parent>
        <ItemID>28</ItemID>
        <Child>
            <ItemID>28</ItemID>
        </Child>
        <Child>
            <ItemID>28</ItemID>
        </Child>
    </Parent>
    <Parent>
        <ItemID>38</ItemID>
        <Child>
            <ItemID>38</ItemID>
        </Child>
    </Parent>
</Root>'

select P.ItemID,
       (
       select P.ItemID
       from P.Child.nodes('Child') as C(N)
       for xml path('Child'), type
       )
from
  (
  select -row_number() over(order by P.N) as ItemID,
         P.N.query('Child') as Child
  from @XML.nodes('/Root/Parent') as P(N)
  ) as P
for xml path('Parent'), root('Root'), type
于 2013-06-28T09:05:22.743 回答
1

Mikael 的解决方案很优雅,但实际上我的 XML 比问题中的要复杂一些(更多元素、一些属性等),因此重新创建 XML 变得更加复杂并且有点维护问题。所以最后我用一些好的旧程序代码解决了这个问题:

declare @XML xml = '<Root>
    <Parent>
        <ItemID>28</ItemID>
        <Child>
            <ItemID>28</ItemID>
        </Child>
        <Child>
            <ItemID>28</ItemID>
        </Child>
    </Parent>
    <Parent>
        <ItemID>18</ItemID>
        <Child>
            <ItemID>18</ItemID>
        </Child>
    </Parent>
</Root>'

DECLARE @CountOfParent INT = @XML.value('count(/Root/Parent)', 'int') 
WHILE @CountOfParent > 0
BEGIN
    PRINT @CountOfParent
    --Replace the current Parent/ItemID element with -@CountOfParent
    SET @XML.modify('replace value of (/Root/Parent/ItemID/text())[sql:variable("@CountOfParent")][1] with -sql:variable("@CountOfParent")')

    --Now loop through all of THIS elements <Child> elements, also setting the ItemID to -@CountOfParent
    DECLARE @CountOfChildren INT = @XML.value('count(/Root/Parent[sql:variable("@CountOfParent")]/Child)', 'int') 
    WHILE @CountOfChildren > 0
    BEGIN
        set @XML.modify('replace value of (/Root/Parent[sql:variable("@CountOfParent")]/Child[sql:variable("@CountOfChildren")]/ItemID/text())[1] with -sql:variable("@CountOfParent")')
        set @CountOfChildren = @CountOfChildren - 1
    END
    SET @CountOfParent = @CountOfParent - 1
END

SELECT @XML

但是,当 Mikael 回答了这个问题时,我已经接受了他的回答,我只是将其包括在内以供参考。

于 2013-06-28T10:38:50.500 回答