Как создать объединенные записи в виде встроенных элементов XML в SELECT... FOR XML?

Я застрял при создании XML-документа (IDOC) из выбора SQL.

XML должен выглядеть так:

<IDOC>
  <MASTER>
    .... some master elements ...
    <NESTED1>
      ... 1st nested element instance 1...
    </NESTED1>
    <NESTED1>
      ... 1st nested element instance 2...
    </NESTED1>
    <NESTED2>
      ... instance 1
    </NESTED2>
    <NESTED2>
      ... instance 2
    </NESTED2>
   </MASTER>
<IDOC>

Но я получаю следующее:

<IDOC>
  <MASTER>
    .... some master elements ...
    <NESTED1>
      ... 1st nested element instance 1...
    </NESTED1>
    <NESTED2>
      ... instance 1
    </NESTED2>
   </MASTER>
<IDOC>
<IDOC>
  <MASTER>
    .... some master elements ...
    <NESTED1>
      ... 1st nested element instance 1...
    </NESTED1>
    <NESTED2>
      ... instance 2
    </NESTED2>
   </MASTER>
<IDOC>
<IDOC>
  <MASTER>
    .... some master elements ...
    <NESTED1>
      ... 1st nested element instance 2...
    </NESTED1>
    <NESTED2>
      ... instance 1
    </NESTED2>
   </MASTER>
<IDOC>
<IDOC>
  <MASTER>
    .... some master elements ...
    <NESTED1>
      ... 1st nested element instance 2...
    </NESTED1>
    <NESTED2>
      ... instance 2
    </NESTED2>
   </MASTER>
<IDOC>

Набор данных SQL, генерирующий XML, выглядит следующим образом:

select 
   MASTER.COL1 as 'MASTER/COL1',
   MASTER.COL2 as 'MASTER/COL2',
   NESTED1.DATA1 as 'MASTER/NESTED1/DATA1',
   NESTED2.TEXT1 as 'MASTER/NESTED2/TEXT1',
from MASTER
join NESTED1 on NESTED1.MASTER_PK = MASTER.MASTER_PK
join NESTED2 on NESTED2.MASTER_PK = MASTER.MASTER_PK
for xml PATH, ROOT('IDOC')

Возможно, я не правильно прочитал документацию, но я не нашел ни одного примера, возможно ли то, что мне нужно, и какие директивы или параметры следует использовать.

Что касается минимально воспроизводимого примера, связанного с https://dbfiddle.uk/eZWVmVKL:

желаемый результат будет:

<MASTER>
 <ID>158</ID>
 <NAME>Antenna</NAME>

  <DESCRIPTION>
    <LANG>CZ</LANG>
    <TEXT>Antena Yagi 6el</TEXT>
  </DESCRIPTION>

  <DESCRIPTION>
    <LANG>EN</LANG>
    <TEXT>Antenna Yagi 6 elements</TEXT>
  </DESCRIPTION>

  <PERFORMANCE>
    <PARAM>GAIN</PARAM>
    <UNIT>dBi</UNIT>
    <VALUE>8.5</VALUE>
  </PERFORMANCE>

  <PERFORMANCE>
   <PARAM>F/B RATIO</PARAM>
    <UNIT>dB</UNIT>
    <VALUE>15</VALUE>
  </PERFORMANCE>

  <PERFORMANCE>
    <PARAM>LENGTH</PARAM>
    <UNIT>cm</UNIT>
    <VALUE>120</VALUE>
  </PERFORMANCE>

</MASTER>

Встраивание последовательных чисел в имена элементов XML считается плохой практикой. Если вам действительно нужны последовательные номера, лучше использовать для этого атрибуты. А также следующее: <root><row id=1>...</row><row id=2>...</row></root>

Yitzhak Khabinsky 06.05.2024 13:23

Недоразумение, @YitzhakKhabinsky. Я создал общий пример, чтобы не разглашать зарезервированную информацию о компании. Например, NESTED1 и NESTED2 могут быть ЧАСТЯМИ и ЦВЕТАМИ. То же самое относится к DATA1, TEXT1. Это примеры имен элементов, не связанные с порядком или значениями данных.

Jindrich Vavruska 06.05.2024 14:06

Задавая вопрос, вам необходимо предоставить минимальный воспроизводимый пример: (1) DDL и образец набора данных, т. е. таблицы CREATE плюс инструкции INSERT T-SQL. (2) Что вам нужно сделать, т. е. логику и попытку реализации вашего кода в T-SQL. (3) Желаемый результат, основанный на примере данных в пункте 1 выше. (4) Версия вашего SQL-сервера (ВЫБЕРИТЕ @@version;).

Yitzhak Khabinsky 06.05.2024 14:13

Пожалуйста, предоставьте минимальный воспроизводимый пример, и мы предоставим вам работающее решение через T-SQL. Ответ, который вы отметили как Ответ, не является Ответом без рабочего кода.

Yitzhak Khabinsky 06.05.2024 14:16

Пожалуйста, предоставьте минимальный воспроизводимый пример: ##1-4. Решением является сочетание T-SQL и XQuery. Мы принимаем вызов!

Yitzhak Khabinsky 07.05.2024 21:40
dbfiddle.uk/eZWVmVKL
Jindrich Vavruska 08.05.2024 06:55

Спасибо за dbfiddle. Увы, он охватывает только ##1,4. Пожалуйста, отредактируйте свой вопрос и добавьте № 3: Желаемый результат на основе примеров данных.

Yitzhak Khabinsky 08.05.2024 15:07

Я предоставил черновой вариант ответа. Все еще ждем желаемого результата.

Yitzhak Khabinsky 08.05.2024 16:06
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
8
62
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Вы не группируете по MASTER.MASTER_PK, поэтому вы получаете 1 строку для каждого кортежа (master, вложенный1, вложенный2), сгенерированного вашими соединениями JOIN. Как только вы сгруппируете по MASTER.MASTER_PK, у вас возникнет еще одна проблема: вы получите перекрестное произведение все NESTED1 и NESTED2 связаны с этим MASTER_PK, поэтому решение состоит в том, чтобы сгенерировать список узлов «NESTED1» и «NESTED2» с подзапросами, агрегирующими результаты в узле MASTER.

P.S. поэтому мне приходится создавать наборы встроенных элементов отдельно, а затем искать другой способ составления окончательного XML; я полагаю, что в MS-SQL это не поддерживается.

Jindrich Vavruska 06.05.2024 14:08

@p3consulting, можно ли переработать ваш ответ и предоставить работающий код T-SQL?

Yitzhak Khabinsky 06.05.2024 14:38

Извините, я недостаточно свободно владею диалектом SQLServer, когда дело касается генерации XML (или JSON).

p3consulting 06.05.2024 14:48

Упрощенная версия для SQLServer: dbfiddle.uk/9ZlkhoRq

p3consulting 06.05.2024 17:32

Попробуйте следующее решение на основе T-SQL и XQuery.

Он следует той же парадигме минимальных воспроизводимых примеров. Вы копируете его в SSMS как есть, запускаете, и он работает как положено, выдавая желаемый результат.

SQL

USE tempdb;
GO

-- DDL and sample data population, start
DROP TABLE IF EXISTS popis;
DROP TABLE IF EXISTS master;
DROP TABLE IF EXISTS performance;

CREATE TABLE master (
   master_pk int identity(1,1) primary key,
   item_id int,
   item_name varchar(50)
);
INSERT INTO master VALUES 
( 158, 'Antenna');

create table popis (
  master_pk int,
  POPIS_PK int identity(1,1) primary key,
  LANG varchar(3),
  TEXT varchar(50)
);
insert into popis values 
(1, 'CZ', 'Antena Yagi 6el'), 
(1, 'EN', 'Antenna Yagi 6 elements');

create table performance ( 
  master_pk int, 
  performance_pk int identity(1,1),
  param varchar(50),
  unit varchar(10),
  value numeric(8,3)
);
insert into performance 
values (1, 'GAIN', 'dBi', 8.5),
(1, 'F/B RATIO', 'dB', 15),
(1, 'LENGTH', 'cm', 120);
-- DDL and sample data population, end

SELECT m.item_id AS [ID], m.item_name AS [NAME]
    , (SELECT po.LANG, po.TEXT
        FROM popis AS po
        WHERE po.master_pk = m.master_pk
        FOR XML PATH('DESCRIPTION'), TYPE)
    , (SELECT pe.param AS [PARAM], pe.unit AS [UNIT], pe.value AS [VALUE]
        FROM performance AS pe
        WHERE pe.master_pk = m.master_pk
        FOR XML PATH('PERFORMANCE'), TYPE)
FROM dbo.master  AS m
FOR XML PATH(''), TYPE, ROOT('MASTER');

Выход

<MASTER>
  <ID>158</ID>
  <NAME>Antenna</NAME>
  <DESCRIPTION>
    <LANG>CZ</LANG>
    <TEXT>Antena Yagi 6el</TEXT>
  </DESCRIPTION>
  <DESCRIPTION>
    <LANG>EN</LANG>
    <TEXT>Antenna Yagi 6 elements</TEXT>
  </DESCRIPTION>
  <PERFORMANCE>
    <PARAM>GAIN</PARAM>
    <UNIT>dBi</UNIT>
    <VALUE>8.500</VALUE>
  </PERFORMANCE>
  <PERFORMANCE>
    <PARAM>F/B RATIO</PARAM>
    <UNIT>dB</UNIT>
    <VALUE>15.000</VALUE>
  </PERFORMANCE>
  <PERFORMANCE>
    <PARAM>LENGTH</PARAM>
    <UNIT>cm</UNIT>
    <VALUE>120.000</VALUE>
  </PERFORMANCE>
</MASTER>

ну это точно НЕ нужный формат так как записей больше одной и мастер повторяется

Jindrich Vavruska 09.05.2024 14:23

Надеюсь, теперь вы понимаете важность минимального воспроизводимого примера. Все четыре №№ 1–4 очень важны. Мне нужен от вас номер 3 – желаемый результат. Пожалуйста, отредактируйте свой исходный вопрос и добавьте все ##1-4 без dbfiddler. Прямо в исходном вопросе.

Yitzhak Khabinsky 09.05.2024 14:29

Была ли у вас возможность опробовать предложенное решение?

Yitzhak Khabinsky 10.05.2024 15:03

Другие вопросы по теме