Как создать XML определенного формата из таблицы MSSQL с помощью FOR XML PATH?
Никак не получается создать XML из таблицы формата, который мне нужен. На хабре нашел вроде хорошую статью по этому вопросу, но даже с ее использованием не выходит, такое ощущение, что я никак не могу понять суть, как вообще работает это xml path. Помогите, кто разбирается
Код создания таблиц для тестов (данные от балды, просто чтобы показать суть того, что я имею и что хочу получить):
if object_id('dbo.TestAutoInfo', 'U') is not null
drop table dbo.TestAutoInfo;
go
create table dbo.TestAutoInfo
(
CityID int
,CarBrand varchar(20)
,CarModel varchar(20)
,CarBodyType varchar(20)
,CarYear smallint
,CarColor varchar(20)
)
go
insert into dbo.TestAutoInfo
(
CityID
,CarBrand
,CarModel
,CarBodyType
,CarYear
,CarColor
)
values
(1283,'Toyota','Corolla','Sedan',2013,'White')
,(1283,'Toyota','Rav4','SUV',2021,'Black')
,(1283,'Ford','Mustang','Coupe',2024,'Red')
go
select CityID
,CarBrand
,CarModel
,CarBodyType
,CarYear
,CarColor
from TestAutoInfo
Примечание к данным - CityID всегда одно и тоже в рамках этого блока
Что я хочу получить из этих данных:
- В идеале вот такое
<data>
<field name="CityID">1283</field>
</data>
<data>
<field name="CarBrand">Toyota</field>
<field name="CarModel">Corolla</field>
<field name="CarBodyType">Sedan</field>
<field name="CarYear">2013</field>
<field name="CarColor">White</field>
</data>
<data>
<field name="CarBrand">Toyota</field>
<field name="CarModel">Rav4</field>
<field name="CarBodyType">SUV</field>
<field name="CarYear">2021</field>
<field name="CarColor">Black</field>
</data>
<data>
<field name="CarBrand">Ford</field>
<field name="CarModel">Mustang</field>
<field name="CarBodyType">Coupe</field>
<field name="CarYear">2024</field>
<field name="CarColor">Red</field>
</data>
Но если не такое, то хотя бы вот такое
<data>
<field name="CityID">1283</field>
<field name="CarBrand">Toyota</field>
<field name="CarModel">Corolla</field>
<field name="CarBodyType">Sedan</field>
<field name="CarYear">2013</field>
<field name="CarColor">White</field>
</data>
<data>
<field name="CityID">1283</field>
<field name="CarBrand">Toyota</field>
<field name="CarModel">Rav4</field>
<field name="CarBodyType">SUV</field>
<field name="CarYear">2021</field>
<field name="CarColor">Black</field>
</data>
<data>
<field name="CityID">1283</field>
<field name="CarBrand">Ford</field>
<field name="CarModel">Mustang</field>
<field name="CarBodyType">Coupe</field>
<field name="CarYear">2024</field>
<field name="CarColor">Red</field>
</data>
Пробовал по разному, но никак не выходит. Если никто не сможет помочь кодом, то может хотя бы накидайте ссылок, пожалуйста, на адекватные статьи как работает этот "%:/** @ XML PATH с кучей примеров, а не просто два-три.
Привожу два решения, которые работают, но которые мне не нравятся по следующим причинам - первое мне не нравится тем, что оно лично на мой вкус оно кривое, да и еще необходим курсор для работоспособности. Второе ничем, по сути, не отличается от ручной сборки XML.
@xml xml
,@xml_f xml
select @xml =
(
select CityID as "CityID"
,CarBrand as "CarBrand"
,CarModel as "CarModel"
,CarBodyType as "CarBodyType"
,CarYear as "CarYear"
,CarColor as "CarColor"
from TestAutoInfo
for xml path('data')
)
--выведем промежуточный XML
select @xml
declare @fields table(
id int not null
,fname varchar(100)
,flabel varchar(100)
)
insert into @fields(id, fname, flabel)
select 1, 'CityID', 'CityID'
union all
select 2, 'CarBrand', 'CarBrand'
union all
select 3, 'CarModel', 'CarModel'
union all
select 4, 'CarBodyType', 'CarBodyType'
union all
select 5, 'CarYear', 'CarYear'
union all
select 6, 'CarColor', 'CarColor'
--Какая-то неизвестная магия
set @xml_f = (
select c.col_name as 'field/@name'
,isnull(@xml.value('(//*[local-name()=sql:column("c.col_name")])[1]','varchar(max)'),'') as 'field'
from (select fname as col_name from @fields) c
for xml path(''), root('data')
)
--Выведем окончательный XML и поймем, что он обрабатывает только один блок, поэтому и нужен курсор
select @xml_f
Второе решение от ИИ следующее
(
SELECT
distinct 'CityID' AS 'field/@name',
CityID AS 'field'
FROM TestAutoInfo
FOR XML PATH('data'), TYPE
)
UNION ALL
(
SELECT
(
SELECT
CarBrand,
CarModel,
CarBodyType,
CarYear,
CarColor
FROM TestAutoInfo
FOR XML PATH('data'), TYPE
).query('
for $d in /data
return
<data>
{
for $c in $d/*
return
<field name="{local-name($c)}">{data($c)}</field>
}
</data>
')
)
FOR XML PATH(''), TYPE```
Ответы (3 шт):
Особо ничего делать не нужно:
SELECT *
FROM [dbo].[TestAutoInfo]
FOR XML PATH ('Data')
Можно повыбирать поля:
SELECT CarBrand,CarModel,CarBodyType,CarYear,CarColor
FROM [dbo].[TestAutoInfo]
FOR XML PATH ('Data')
Это:
<data>
<field name="CityID">1283</field>
</data>
<data>
<field name="CarBrand">Toyota</field>
...
неправильные выходные данные, может быть как-то так:
<City>
<field name="CityID">1283</field>
<data>
<field name="CarBrand">Toyota</field>
</data>
<data>
<field name="CarBrand">Toyota</field>
</data>
</City>
Как это сделать - читайте тут.
Попробуйте следующее решение.
Если корневой элемент не нужен, просто закомментируйте , ROOT('root').
SQL
DECLARE @TestAutoInfo TABLE
(
CityID int
,CarBrand varchar(20)
,CarModel varchar(20)
,CarBodyType varchar(20)
,CarYear smallint
,CarColor varchar(20)
);
INSERT @TestAutoInfo
(
CityID
,CarBrand
,CarModel
,CarBodyType
,CarYear
,CarColor
)
values
(1283,'Toyota','Corolla','Sedan',2013,'White'),
(1283,'Toyota','Rav4','SUV',2021,'Black'),
(1283,'Ford','Mustang','Coupe',2024,'Red');
SELECT 'CityID' AS [field/@name]
, CityID AS [field]
, ''
, 'CarBrand' AS [field/@name]
, CarBrand AS [field]
, ''
, 'CarModel' AS [field/@name]
, CarModel AS [field]
, ''
, 'CarBodyType' AS [field/@name]
, CarBodyType AS [field]
, ''
, 'CarYear' AS [field/@name]
, CarYear AS [field]
, ''
, 'CarColor' AS [field/@name]
, CarColor AS [field]
FROM @TestAutoInfo
FOR XML PATH('data'), TYPE, ROOT('root');
Результат
<root>
<data>
<field name="CityID">1283</field>
<field name="CarBrand">Toyota</field>
<field name="CarModel">Corolla</field>
<field name="CarBodyType">Sedan</field>
<field name="CarYear">2013</field>
<field name="CarColor">White</field>
</data>
<data>
<field name="CityID">1283</field>
<field name="CarBrand">Toyota</field>
<field name="CarModel">Rav4</field>
<field name="CarBodyType">SUV</field>
<field name="CarYear">2021</field>
<field name="CarColor">Black</field>
</data>
<data>
<field name="CityID">1283</field>
<field name="CarBrand">Ford</field>
<field name="CarModel">Mustang</field>
<field name="CarBodyType">Coupe</field>
<field name="CarYear">2024</field>
<field name="CarColor">Red</field>
</data>
</root>
SELECT CAST(N'' AS XML).query('
element data {
element field { attribute name {"CityId"}, text {sql:column("CityId")} },
element field { attribute name {"CarBrand"}, text {sql:column("CarBrand")} },
element field { attribute name {"CarModel"}, text {sql:column("CarModel")} },
element field { attribute name {"CarBodyType"}, text {sql:column("CarBodyType")} },
element field { attribute name {"CarYear"}, text {sql:column("CarYear")} },
element field { attribute name {"CarColor"}, text {sql:column("CarColor")} }
}')
FROM TestAutoInfo
Конструируем XML фактически вручную, но зато очень наглядно.
XML будет не единым целым, а по строкам.
Узел с CityId можно получать отдельно:
SELECT CAST(N'' AS XML).query('
element data {
element field { attribute name {"CityId"}, text {sql:column("CityId")} }
}')
FROM TestAutoInfo
GROUP BY CityId
UNION ALL
SELECT CAST(N'' AS XML).query('
element data {
element field { attribute name {"CarBrand"}, text {sql:column("CarBrand")} },
element field { attribute name {"CarModel"}, text {sql:column("CarModel")} },
element field { attribute name {"CarBodyType"}, text {sql:column("CarBodyType")} },
element field { attribute name {"CarYear"}, text {sql:column("CarYear")} },
element field { attribute name {"CarColor"}, text {sql:column("CarColor")} }
}')
FROM TestAutoInfo
Впрочем, можно ещё проще:
SELECT CAST(N'' AS XML).query('
<data>
<field name="CarBrand"> {sql:column("CarBrand")} </field>
<field name="CarModel"> {sql:column("CarModel")} </field>
<field name="CarCarBodyType">{sql:column("CarBodyType")}</field>
<field name="CarYear"> {sql:column("CarYear")} </field>
<field name="CarColor"> {sql:column("CarColor")} </field>
</data>
')
FROM TestAutoInfo