Запрос узлов XML в MS SQL - PullRequest
1 голос
/ 27 мая 2019

У меня есть следующая запись XML.

<row>
  <c1>BUSINESS.HOME</c1>
  <c1 m="2">PAYMENTS.HOME</c1>
  <c1 m="3">DEPARTMENT.PAGE</c1>
  <c1 m="4">SECTION.HOME</c1>
  <c1 m="5">ABOUT.HOME</c1>

  <c2>Our Business</c2>
  <c2 m="1" s="2">Businesul nostru</c2>
  <c2 m="2">Payment Services</c2>
  <c2 m="2" s="2">Plati</c2>
  <c2 m="3">Department Operations</c2>
  <c2 m="3" s="2">Departamente</c2>
  <c2 m="4">Section Operations</c2>
  <c2 m="4" s="2">Sectiuni</c2>
  <c2 m="5">ABOUT</c2>
  <c2 m="5" s="2">Despre</c2>

  <c6>2</c6>
  <c10>GB0010001</c10>
  <c11>1</c11>
</row>

C2 - метки для C1 на двух языках, различающихся по атрибуту S, поэтому счет S2 всегда является двойным C1.

Я бы хотел выбрать запрос со следующим выводом.

NAME              LABEL
--------------------------------------
BUSINESS.HOME     Our Business
PAYMENTS.HOME     Payment Services
DEPARTMENT.PAGE   Department Operations
SECTION.HOME      Section Operations
ABOUT.HOME        ABOUT

Каждое значение из C1 и соответствующее первое значение из C2.

Я пробовал применять кросс-аппликации так:

select t.p.query('.').value('data(./c2)[1]', 'varchar(max)') c2
from table_name 
cross apply XMLRECORD.nodes('(/row/c2)') t(p)
where ID  = 'HOME.PAGE'

Но не знаете, как действовать дальше, чтобы исключить иностранный язык и включить тег C1.

Ответы [ 2 ]

2 голосов
/ 27 мая 2019

Это немного "некрасиво" из-за необходимости совпадать с NULL на NULL, однако, вот как я бы достиг того, что вы ищете:

DECLARE @XML xml = '
<row>
  <c1>BUSINESS.HOME</c1>
  <c1 m="2">PAYMENTS.HOME</c1>
  <c1 m="3">DEPARTMENT.PAGE</c1>
  <c1 m="4">SECTION.HOME</c1>
  <c1 m="5">ABOUT.HOME</c1>

  <c2>Our Business</c2>
  <c2 m="1" s="2">Businesul nostru</c2>
  <c2 m="2">Payment Services</c2>
  <c2 m="2" s="2">Plati</c2>
  <c2 m="3">Department Operations</c2>
  <c2 m="3" s="2">Departamente</c2>
  <c2 m="4">Section Operations</c2>
  <c2 m="4" s="2">Sectiuni</c2>
  <c2 m="5">ABOUT</c2>
  <c2 m="5" s="2">Despre</c2>

  <c6>2</c6>
  <c10>GB0010001</c10>
  <c11>1</c11>
</row>';

SELECT c1.value('(./text())[1]','varchar(15)') AS [NAME],
       c2.value('(./text())[1]','varchar(15)') AS LABEL
FROM @XML.nodes('row/c1') c1(c1)
     JOIN @XML.nodes('row/c2') c2(c2) ON (c1.value('@m','int') = c2.value('@m','int') AND c2.value('@s','int') IS NULL)
                                      OR (c1.value('@m','int') IS NULL AND c2.value('@m','int') IS NULL);
0 голосов
/ 28 мая 2019

У вас уже есть рабочий подход.Это нормально.

Я просто хочу добавить альтернативу, которая должна быть лучше по производительности.

DECLARE @XML xml = '
<row>
  <c1>BUSINESS.HOME</c1>
  <c1 m="2">PAYMENTS.HOME</c1>
  <c1 m="3">DEPARTMENT.PAGE</c1>
  <c1 m="4">SECTION.HOME</c1>
  <c1 m="5">ABOUT.HOME</c1>

  <c2>Our Business</c2>
  <c2 m="1" s="2">Businesul nostru</c2>
  <c2 m="2">Payment Services</c2>
  <c2 m="2" s="2">Plati</c2>
  <c2 m="3">Department Operations</c2>
  <c2 m="3" s="2">Departamente</c2>
  <c2 m="4">Section Operations</c2>
  <c2 m="4" s="2">Sectiuni</c2>
  <c2 m="5">ABOUT</c2>
  <c2 m="5" s="2">Despre</c2>

  <c6>2</c6>
  <c10>GB0010001</c10>
  <c11>1</c11>
</row>';

- Запрос

WITH Tally(Nmbr) AS(SELECT TOP(@Xml.value('count(/row/c1)','int')) ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) FROM master..spt_values )
SELECT @Xml.value('(/row/c1/text())[sql:column("Nmbr")][1]','nvarchar(250)') AS [NAME]
      ,@xml.value('(/row/c2/text())[sql:column("Nmbr")*2-1][1]','nvarchar(250)') AS [LABEL]
FROM Tally;

Идея вкратце:

  • Создать подсчет на лету , возвращающий список от 1 до n, где n - количество элементов <c1> (если у вас есть таблица чиселиспользуйте это вместо этого)
  • Используйте столбец Tally Nmbr и введите его в предикат XQuery с помощью sql:column().
    • Первый .value() вызывает значение <c1> по своей позиции.
    • Второй вызывает соответствующее значение по позиции x2 -1 .без «-1» вы получите другой язык.

Это еще один подход - фильтрация элементов <c2> без атрибута s.В этом случае нам не нужно вычисление с position x2 -1 :

WITH Tally(Nmbr) AS(SELECT TOP(@Xml.value('count(/row/c1)','int')) ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) FROM master..spt_values )
SELECT @Xml.value('(/row/c1/text())[sql:column("Nmbr")][1]','nvarchar(250)') AS [NAME]
      ,@xml.value('(/row/c2[empty(@s)]/text())[sql:column("Nmbr")][1]','nvarchar(250)') AS [LABEL]
FROM Tally 
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...