2016-09-08 2 views
2

У меня есть XML, как показано ниже:использование переменной в XPath для SQL сервера

<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][email protected]) 

      -- 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> и так далее получать данные из другой таблицы, которые хранят имя тега.

Как это сделать?

+0

Вместо 'SQL: variable' вы можете присоединиться к таблице, что нужно и использовать' SQL: column' – gofr1

+0

я дон У меня есть таблица, у меня есть только xml – zanza67

+0

Можете ли вы показать, какой результат вы хотите получить из XML? Почему вы говорите, что у вас нет таблицы, если вы используете это для получения значения для обновления: 'select [Target Code] из [Ronal]. [Dbo]. ['Value Mapping $'] где [Имя списка] = 'Национальность 'и [SAP Code] = @ nodevalue' – gofr1

ответ

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> 
+0

. Большое вам спасибо gofr1, но в вашем случае вы жестко читаете PA0002_NATIO, чего я хочу избежать. то, что я хочу достичь, является версией параметра замены, где значение, которое нужно заменить для тега, зависит от динамического xpath, который я хочу построить. например, я хочу заменить значение для Record \ PA0002_NATIO, но также и для записи \ PA0001_CITY, поэтому, чтобы сделать то, что мне нужно писать дважды, моя идея - получить PA0002_NATIO и PA0001_CITY где-нибудь (например, таблицу) и предоставить во время выполнения переменную xpath, чтобы заменить значение. возможно, я не объяснил правильно, извините за это – zanza67

+0

Проверьте мои изменения. '@nodename nvarchar (max) = 'PA0002_NATIO'' в имени узла узла' @ nodename', в котором вы нуждаетесь. – gofr1

+0

спасибо, я посмотрю – zanza67

Смежные вопросы