在xpath中为sql server使用变量

时间:2016-09-08 12:28:04

标签: sql-server variables xpath

我有如下的XML:

<BODY>
    <RECORD>
        <PA0002_NATIO>CH</PA0002_NATIO>
        <PA0001_CITY>Lugano</PA0001_CITY>
        <PA0005_VALUE>1000</PA0005_VALUE>
    </RECORD>
    <RECORD>
        <PA0002_NATIO>DE</PA0002_NATIO>
        <PA0001_CITY>Berlin</PA0001_CITY>
        <PA0005_VALUE>2000</PA0005_VALUE>
    </RECORD>
    <RECORD>
        <PA0002_NATIO>IT</PA0002_NATIO>
        <PA0001_CITY>Roma</PA0001_CITY>
        <PA0005_VALUE>3000</PA0005_VALUE>
    </RECORD>
</BODY>

我想在所有<PA0002_NATIO>个节点中更改标记<RECORD>的值,为了做到这一点,我计算了<RECORD>个节点的数量,我做了一个循环这个,新值取自表格。

if @countNodes > 0
    begin
    set @indexCount = 1
    while @indexCount <= @countNodes
        begin           

            -- get the value from the node          
            set @nodevalue = (@xml.value('(//RECORD[sql:variable("@indexCount")]/PA0002_NATIO/text())[1]', 'nvarchar(50)'))

            -- find in the table the value to be replaced
            set @repvalue = (select [Target Code] from [Ronal].[dbo].['Value Mapping$']
            where [List Name]='Nationality' and [SAP Code]=@nodevalue)

            -- replace the value in the node
            set @xml.modify('
            replace value of 
                (//RECORD[sql:variable("@indexCount")]/PA0002_NATIO/text())[1] 
            with
                sql:variable("@repvalue")
            ');


            SET @Indexcount=  @Indexcount + 1;
        end
end 

END

现在的想法是使用xpath中的变量进行泛型替换 而不是使用

set @nodevalue = (@xml.value('(//RECORD[sql:variable("@indexCount")]/PA0002_NATIO/text())[1]', 'nvarchar(50)'))

我会用

set @nodevalue = (@xml.value('(//RECORD[sql:variable("@indexCount")]/[sql:variable("@tag")]/text())[1]', 'nvarchar(50)'))

当然我会使用相同的语法来替换

-- replace the value in the node
set @xml.modify('
replace value of 
    (//RECORD[sql:variable("@indexCount")]/[sql:variable("@tag")]/text())[1] 
with
    sql:variable("@repvalue")
');

@tag变量包含<PA0002_NATIO><PA0001_CITY>等等,从另一个存储标记名称的表中获取数据。

我该怎么做?

1 个答案:

答案 0 :(得分:1)

你需要一个棘手的.modify循环:

--declare table with names and ids like you posted in comment
DECLARE @test TABLE (
    Name nvarchar(2),
    id nvarchar(2)
)

INSERT INTO @test VALUES
('CH', '00'),
('DE', '01'),
('IT', '02')


DECLARE @xml XML = '
    <BODY>
            <RECORD>
                <PA0002_NATIO>CH</PA0002_NATIO>
                <PA0001_CITY>Lugano</PA0001_CITY>
                <PA0005_VALUE>1000</PA0005_VALUE>
            </RECORD>
            <RECORD>
                <PA0002_NATIO>DE</PA0002_NATIO>
                <PA0001_CITY>Berlin</PA0001_CITY>
                <PA0005_VALUE>2000</PA0005_VALUE>
            </RECORD>
            <RECORD>
                <PA0002_NATIO>IT</PA0002_NATIO>
                <PA0001_CITY>Roma</PA0001_CITY>
                <PA0005_VALUE>3000</PA0005_VALUE>
            </RECORD>
    </BODY>';

DECLARE @Counter int = 1,
        @newValue nvarchar(max),
        @nodename nvarchar(max) ='PA0002_NATIO'

WHILE @Counter <= @xml.value('fn:count(//*//*//*[local-name()=sql:variable("@nodename")])','int')
BEGIN
    SELECT @newValue = id
    FROM @test
    WHERE Name = CAST(@xml.query('((/*/*/*[local-name()=sql:variable("@nodename")])[position()=sql:variable("@Counter")]/text())[1]') as nvarchar(2))

    SET @xml.modify('replace value of ((/*/*/*[local-name()=sql:variable("@nodename")])[position()=sql:variable("@Counter")]/text())[1] with sql:variable("@newValue")')

    SET @Counter = @Counter + 1;
END

SELECT  @xml; 

输出:

<BODY>
  <RECORD>
    <PA0002_NATIO>00</PA0002_NATIO>
    <PA0001_CITY>Lugano</PA0001_CITY>
    <PA0005_VALUE>1000</PA0005_VALUE>
  </RECORD>
  <RECORD>
    <PA0002_NATIO>01</PA0002_NATIO>
    <PA0001_CITY>Berlin</PA0001_CITY>
    <PA0005_VALUE>2000</PA0005_VALUE>
  </RECORD>
  <RECORD>
    <PA0002_NATIO>02</PA0002_NATIO>
    <PA0001_CITY>Roma</PA0001_CITY>
    <PA0005_VALUE>3000</PA0005_VALUE>
  </RECORD>
</BODY>