如何在SQL Server中替换XML查询?

时间:2017-07-18 14:49:23

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

我有一个关于替换XML列的问题,这是一个关于我想要改变的小例子。

<i><recipes n="0" />
<CGrecipes cg="4" r0="302053" r1="302084" r2="302049" r3="302068" />
<HArecipes ha="4" r0="302103" r1="302083" r2="302050" r3="302087" />
<KHrecipes kh="10" r0="302100" r1="302090" r2="302078" r3="302074" 
                   r4="302094" r5="302082" r6="302066" r7="302051" 
                   r8="302086" r9="302070" />
<KHNrecipes khn="10" r0="302102" r1="302089" r2="302056" r3="302077" 
                     r4="302052" r5="302069" r6="302081" r7="302073" 
                     r8="302093" r9="302085" />
<IMITARrecipes imitar="2" r0="302110" r1="302057" />
<MAUSERrecipes mauser="1" r0="302106" />
<SVDrecipes svd="1" r0="302059" />
<BLASERrecipes blaser="2" r0="302105" r1="302060" />
<SIGSAUERrecipes sigsauer="2" r0="302109" r1="302061" />
<HONEYBADGERrecipes honeybadger="1" r0="302062" />
<SMALLBACKPACKrecipes smallbackpack="1" r0="302095" />
<MEDIUMBACKPACKrecipes mediumbackpack="1" r0="302096" />
<MILITARYBACKPACKrecipes militarybackpack="1" r0="302097" />
<LARGEBACKPACKrecipes largebackpack="1" r0="302098" />
<TEDDYBACKPACKrecipes teddybackpack="1" r0="302099" />
<ALICEBACKPACKrecipes alicebackpack="1" r0="302112" />
<M82recipes m82="1" r0="302107" />
<AWMrecipes awm="1" r0="302108" />
<B93Rrecipes b93r="1" r0="302111" />
</i>

我想用脚本更改它:

<i><recipes r="0" r0="302053" r1="302084" r2="302049" r3="302068" r4="302103" r5="302083" r6="302050" r7="302087" r8="302100" r9="302090" r10="302078" r11="302074" r12="302094" r13="302082" r14="302066" r15="302051" r16="302086" r17="302070" r18="302102" r19="302089" r20="302056" r21="302077" r22="302052" r23="302069" r24="302081" r25="302073" r26="302093" r27="302085" r28="302110" r29="302057" r30="302106" r31="302059" r32="302105" r33="302060" r34="302109" r35="302061" r36="302062" r37="302095" r38="302096" r39="302097" r40="302098" r41="302099" r42="302112" r43="302107" r44="302108" r45="302111" /></i>

我想得到帮助和建议!

1 个答案:

答案 0 :(得分:1)

嗯,这可能不是更简单(字面意思,我认为),这要归功于T-SQL非常有限的XQuery实现,以及对任何类型的动态XML的普遍仇恨。让@xml在变量中包含您的XML(如果它是一列,请根据需要添加FROM。)

SELECT CONVERT(XML, REPLACE((
    SELECT @xml.value('/i[1]/recipes[1]/@n', 'int') AS [@r], '' AS [@marker]
    FOR XML PATH('recipes'), ROOT('i')
), 'marker=""', (
    SELECT ' ' + REPLACE(REPLACE('r#="$v"', 
        '#', ROW_NUMBER() OVER (ORDER BY (SELECT 1)) - 1), 
        '$v', t.value('text()[1]', 'int')) 
    FROM (
        SELECT @xml.query('
            for $a in //*/@*[substring(local-name(),1,1)="r"] 
            return <r>{string($a)}</r>
        ') AS a
    ) _ CROSS APPLY a.nodes('r') AS x(t)
    FOR XML PATH('')
)))

从内部到外部:我们将所有r*属性解包到元素中,将行号附加到它们,然后通过连接字符串将结果折叠回XML。对于结局,我们将n的{​​{1}}属性转换为recipes,并将字符串连接替换为外部元素。

为什么这段代码太可怕了?因为数据模型很糟糕(好吧,因为SQL Server的XQuery实现非常有限,省略了可以简化这一过程的大多数高级功能)。它在各方面都滥用XML。考虑将属性更改为子元素。不要使用r之类的连接元素名称,将其概括为ALICEBACKPACKrecipes或类似名称。想想静态名称重复内容

recipes name='ALICEBACKPACK'

对于任何不是完全成熟的编程语言的东西,查询和处理起来要容易得多。