Создание динамических имен тегов XML для вывода из SQL

(Это продолжение Управление именами тегов и структурой вывода в SQL, на которое хорошо ответил Ицхак Хабинский)

Я пытаюсь извлечь данные из Microsoft SQL Server 2016 в определенных форматах XML. Я могу приблизиться к стандартным операторам FOR XML, но у меня нет опыта использования XQuery, который, как я подозреваю, лежит в основе элегантного решения.

-- DDL and sample data population, start
DECLARE @tbl TABLE (ID INT IDENTITY PRIMARY KEY, FirstName 
VARCHAR(20), MiddleName VARCHAR(20), LastName VARCHAR(20));
INSERT @tbl (FirstName, MiddleName, LastName) VALUES
('Fred', 'A.','Smith'),
('Anna', NULL,'Polack');
-- DDL and sample data population, end

select * from @tbl

;WITH rs(x) AS
(
    SELECT * 
    FROM @tbl
    FOR XML PATH(''), TYPE, ROOT('root')
)
SELECT
 x.query('for $x in /root/*
        return <Answer name = "{local-name($x)}">
          <value>{data($x)}</value>
        </Answer>') AS Result 
FROM rs

Производит следующее:

<Answer name = "ID">
  <value>1</value>
</Answer>
<Answer name = "FirstName">
  <value>Fred</value>
</Answer>
<Answer name = "MiddleName">
  <value>A.</value>
</Answer>
<Answer name = "LastName">
  <value>Smith</value>
</Answer>
<Answer name = "ID">
  <value>2</value>
</Answer>
<Answer name = "FirstName">
  <value>Anna</value>
</Answer>
<Answer name = "LastName">
  <value>Polack</value>
</Answer>

Я хотел бы получить более явное перечисление вывода, например следующее:

<Answer name = "ID_1">
  <value>1</value>
</Answer>
<Answer name = "FirstName_1">
  <value>Fred</value>
</Answer>
<Answer name = "MiddleName_1">
  <value>A.</value>
</Answer>
<Answer name = "LastName_1">
  <value>Smith</value>
</Answer>
<Answer name = "ID_2">
  <value>2</value>
</Answer>
<Answer name = "FirstName_2">
  <value>Anna</value>
</Answer>
<Answer name = "LastName_2">
  <value>Polack</value>
</Answer>

Я могу взломать его, используя:

select STRING_AGG('<Answer name = "'+ColKey+'">'+ 
'<value>'+ColValue+'</value></Answer>',' ')
from
(
select  tab.* from @tbl
CROSS APPLY (
VALUES  ('FirstName_'+cast(ID as varchar), FirstName),
('LastName_'+cast(ID as varchar), LastName)
) tab(ColKey, ColValue)
)t

Но я уверен, что есть более элегантный способ использования XQuery.

К вашему сведению, Stack Overflow не место, чтобы спрашивать «по частям»; каждый вопрос должен быть завершен в его собственных достоинствах. Вы действительно должны указать здесь лучший заголовок, чем; это читается так, как будто нам нужно иметь возможность обратиться к обоим сообщениям.

Thom A 30.03.2023 21:16
ReactJs | Supabase | Добавление данных в базу данных
ReactJs | Supabase | Добавление данных в базу данных
Это и есть ваш редактор таблиц в supabase.👇
Понимание Python и переход к SQL
Понимание Python и переход к SQL
Перед нами лабораторная работа по BloodOath:
1
1
58
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

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

Нет необходимости в string_agg()

Пример

Select  [Answer/@name] = concat('ID','_',id)
       ,[Answer/value] = id
       ,null
       ,[Answer/@name] = concat('FirstName','_',id)
       ,[Answer/value] = FirstName
       ,null
       ,[Answer/@name] = concat('MiddleName','_',id)
       ,[Answer/value] = MiddleName
       ,null
       ,[Answer/@name] = concat('LastName','_',id)
       ,[Answer/value] = LastName
 From   @tbl
 For xml path('')

Полученные результаты

<Answer name = "ID_1">
  <value>1</value>
</Answer>
<Answer name = "FirstName_1">
  <value>Fred</value>
</Answer>
<Answer name = "MiddleName_1">
  <value>A.</value>
</Answer>
<Answer name = "LastName_1">
  <value>Smith</value>
</Answer>
<Answer name = "ID_2">
  <value>2</value>
</Answer>
<Answer name = "FirstName_2">
  <value>Anna</value>
</Answer>
<Answer name = "MiddleName_2" />
<Answer name = "LastName_2">
  <value>Polack</value>
</Answer>

ОБНОВЛЕНИЕ: для многих столбцов. Поскольку у вас есть string_agg()... Я могу предположить, что у вас есть OPENJSON.

Пример

Select [Answer/@name] = concat([key],'_',A.ID)
      ,[Answer/value] = B.value
 From  @tbl A
  Cross Apply  (
                 Select [Key]
                       ,Value
                 From OpenJson(  (Select A.* For JSON Path,Without_Array_Wrapper )  ) 
              ) B
 For xml path('')

Спасибо, Джон. Однако в моей конечной таблице много столбцов, поэтому я надеялся найти способ избежать необходимости называть каждый из них конкретно. С вашим подходом есть ли способ избежать нулей? (например, Отчество_2)?

AA23ds 30.03.2023 21:55

Стоит отметить. Дай мне минуту

John Cappelletti 30.03.2023 21:56

Спасибо, Джон. Очень понятно - помечено как ответ

AA23ds 30.03.2023 22:15

Вы можете использовать concat в XQuery, чтобы добавить к нему значение ID.

Вам просто нужно разбить первый сгенерированный XML на два уровня

WITH rs(x) AS
(
    SELECT *
    FROM @tbl
    FOR XML PATH('row'), TYPE
)
SELECT
 x.query('for $x in /row
          for $y in $x/*
        return <Answer name = "{concat(local-name($y), "_", ($x/ID/text())[1])}">
          <value>{data($y)}</value>
        </Answer>') AS Result 
FROM rs;

дб<>рабочий пример

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