T-SQL. Формирование XML со списком значений

от автора

Небольшая заметка по формированию XML

FOR XML PATH

Для формирования структуры XML-документа со списком значений можно воспользоваться режимом PATH для FOR XML в T-SQL.

<root>     <level1>         <level2></level2>         <values>             <value></value>             <value></value>              <value></value>             <value></value>             <value></value>         </values>      </level1>      <level1>          <level2></level2>          <values>              <value></value>              <value></value>              <value></value>              <value></value>              <value></value>          </values>      </level1> </root> 


Ниже представлен sql код создания тестовых таблиц для демонстрации запросов:

if object_id('dbo.ProductClass', 'U') is not null     drop table dbo.ProductClass; go  create table dbo.ProductClass(     ProductClassId int identity    ,ProductClassName nvarchar(16)    ,constraint PK_ProductClass primary key(ProductClassId)); go  insert into dbo.ProductClass(ProductClassName)     values         ('Class_1')        ,('Class_2')        ,('Class_3'); go  select * from dbo.ProductClass; 

1	Class_1 2	Class_2 3	Class_3 

Запросы будут задействовать две таблицы dbo.ProductClass и dbo.Product со связью «один ко многим»(в одном классе может быть более одного продукта).

if object_id('dbo.Product', 'U') is not null     drop table dbo.Product; go  create table dbo.Product(     ProductClassId int     ,ProductId int identity     ,ProductName nvarchar(16)     ,constraint PK_Product primary key(ProductId)     ,constraint FK_ProductClass foreign key(ProductClassId)         references dbo.ProductClass(ProductClassId)); go  insert into dbo.Product(ProductClassId, ProductName)     values         (1, 'Product_1')         ,(2, 'Product_2')         ,(3, 'Product_3')         ,(1, 'Product_4')         ,(2, 'Product_5')         ,(3, 'Product_6')         ,(1, 'Product_7')         ,(3, 'Product_8')         ,(2, 'Product_9')         ,(1, 'Product_10'); go  select * from dbo.Product; 

1	1	Product_1 2	2	Product_2 3	3	Product_3 1	4	Product_4 2	5	Product_5 3	6	Product_6 1	7	Product_7 3	8	Product_8 2	9	Product_9 1	10	Product_10 

Следующий запрос позволяет построить XML документ со списком значений ProductName таблицы dbo.Product для каждого класса продукта(ProductClassName) в одноименных элементах:

select pc.ProductClassName as ProductClassName     ,(select p.ProductName as ProductName         from dbo.Product p     where p.ProductClassId = pc.ProductClassId         for xml path(''), type) as "Products" from dbo.ProductClass pc     for xml path('ProductClass'), type 

Список значений в элементе Products формируется подзапросом:

(select p.ProductName as ProductName     from dbo.Product p where p.ProductClassId = pc.ProductClassId     for xml path(''), type) as "Products") 

Псевдонимы определяют «имена» XML элементов. Директива TYPE позволяет получить результат запроса в виде типа данных xml. Для того что бы не формировался родительский элемент для списка значений элементов Product.ProductName, значение PATH не указывается(for xml path(»)).

Результатом запроса будет следующий XML-документ:

<ProductClass>     <ProductClassName>Class_1</ProductClassName>     <Products>         <ProductName>Product_1</ProductName>         <ProductName>Product_4</ProductName>         <ProductName>Product_7</ProductName>         <ProductName>Product_10</ProductName>     </Products> </ProductClass> <ProductClass>     <ProductClassName>Class_2</ProductClassName>     <Products>         <ProductName>Product_2</ProductName>         <ProductName>Product_5</ProductName>         <ProductName>Product_9</ProductName>     </Products> </ProductClass> <ProductClass>     <ProductClassName>Class_3</ProductClassName>     <Products>         <ProductName>Product_3</ProductName>         <ProductName>Product_6</ProductName>         <ProductName>Product_8</ProductName>     </Products> </ProductClass> 

Для создания списка со значениями в атрибутах элемента, псевдонимы указываются в кавычках(одинарные или двойные) с символом ‘@’ в начале:

select pc.ProductClassName as ProductClassName     ,(select p.ProductId as "@ProductId", p.ProductName as '@ProductName'         from dbo.Product p     where p.ProductClassId = pc.ProductClassId         for xml path('Product'), type) as "Products" from dbo.ProductClass pc     for xml path('ProductClass'), type 

Значение PATH есть «имя» элемента в котором содержаться атрибуты, результ запроса следующий:

<ProductClass>     <ProductClassName>Class_1</ProductClassName>     <Products>         <Product ProductId="1" ProductName="Product_1" />         <Product ProductId="4" ProductName="Product_4" />         <Product ProductId="7" ProductName="Product_7" />         <Product ProductId="10" ProductName="Product_10" />     </Products> </ProductClass> <ProductClass>     <ProductClassName>Class_2</ProductClassName>     <Products>         <Product ProductId="2" ProductName="Product_2" />         <Product ProductId="5" ProductName="Product_5" />         <Product ProductId="9" ProductName="Product_9" />     </Products> </ProductClass> <ProductClass>     <ProductClassName>Class_3</ProductClassName>     <Products>         <Product ProductId="3" ProductName="Product_3" />         <Product ProductId="6" ProductName="Product_6" />         <Product ProductId="8" ProductName="Product_8" />     </Products> </ProductClass> 

Атрибуты можно разместить в отдельных элементах:

select pc.ProductClassName as ProductClassName     ,(select p.ProductId as "@ProductId", p.ProductName as ProductName         from dbo.Product p     where p.ProductClassId = pc.ProductClassId         for xml path('Product'), type) as "Products" from dbo.ProductClass pc     for xml path('ProductClass'), type 

Результат запроса:

<ProductClass>   <ProductClassName>Class_1</ProductClassName>   <Products>     <Product ProductId="1">       <ProductName>Product_1</ProductName>     </Product>     <Product ProductId="4">       <ProductName>Product_4</ProductName>     </Product>     <Product ProductId="7">       <ProductName>Product_7</ProductName>     </Product>     <Product ProductId="10">       <ProductName>Product_10</ProductName>     </Product>   </Products> </ProductClass> <ProductClass>   <ProductClassName>Class_2</ProductClassName>   <Products>     <Product ProductId="2">       <ProductName>Product_2</ProductName>     </Product>     <Product ProductId="5">       <ProductName>Product_5</ProductName>     </Product>     <Product ProductId="9">       <ProductName>Product_9</ProductName>     </Product>   </Products> </ProductClass> <ProductClass>   <ProductClassName>Class_3</ProductClassName>   <Products>     <Product ProductId="3">       <ProductName>Product_3</ProductName>     </Product>     <Product ProductId="6">       <ProductName>Product_6</ProductName>     </Product>     <Product ProductId="8">       <ProductName>Product_8</ProductName>     </Product>   </Products> </ProductClass> 

Product.ProductName как значение элемента Product и Product.ProductId как значение атрибута этого же элемента:

select pc.ProductClassName as ProductClassName     ,(select p.ProductId as "@ProductId" 	,(select pn.ProductName  		from dbo.Product pn 	where pn.ProductId = p.ProductId)         from dbo.Product p     where p.ProductClassId = pc.ProductClassId         for xml path('Product'), type) as "Products" from dbo.ProductClass pc     for xml path('ProductClass'), type 

Результат:

<ProductClass>   <ProductClassName>Class_1</ProductClassName>   <Products>     <Product ProductId="1">Product_1</Product>     <Product ProductId="4">Product_4</Product>     <Product ProductId="7">Product_7</Product>     <Product ProductId="10">Product_10</Product>   </Products> </ProductClass> <ProductClass>   <ProductClassName>Class_2</ProductClassName>   <Products>     <Product ProductId="2">Product_2</Product>     <Product ProductId="5">Product_5</Product>     <Product ProductId="9">Product_9</Product>   </Products> </ProductClass> <ProductClass>   <ProductClassName>Class_3</ProductClassName>   <Products>     <Product ProductId="3">Product_3</Product>     <Product ProductId="6">Product_6</Product>     <Product ProductId="8">Product_8</Product>   </Products> </ProductClass> 

Использование конструкции WITH XMLNAMESPACES позволяет добавить пространства имен в XML:

WITH XMLNAMESPACES ('uri1' as lev)   select pc.ProductClassName as "lev:ProductClassName"     ,(select p.ProductName as "lev:ProductName"         from dbo.Product p     where p.ProductClassId = pc.ProductClassId         for xml path(''), type) as "lev:Products" from dbo.ProductClass pc     for xml path('lev:ProductClass'), type 

<lev:ProductClass xmlns:lev="uri1">   <lev:ProductClassName>Class_1</lev:ProductClassName>   <lev:Products>     <lev:ProductName xmlns:lev="uri1">Product_1</lev:ProductName>     <lev:ProductName xmlns:lev="uri1">Product_4</lev:ProductName>     <lev:ProductName xmlns:lev="uri1">Product_7</lev:ProductName>     <lev:ProductName xmlns:lev="uri1">Product_10</lev:ProductName>   </lev:Products> </lev:ProductClass> <lev:ProductClass xmlns:lev="uri1">   <lev:ProductClassName>Class_2</lev:ProductClassName>   <lev:Products>     <lev:ProductName xmlns:lev="uri1">Product_2</lev:ProductName>     <lev:ProductName xmlns:lev="uri1">Product_5</lev:ProductName>     <lev:ProductName xmlns:lev="uri1">Product_9</lev:ProductName>   </lev:Products> </lev:ProductClass> <lev:ProductClass xmlns:lev="uri1">   <lev:ProductClassName>Class_3</lev:ProductClassName>   <lev:Products>     <lev:ProductName xmlns:lev="uri1">Product_3</lev:ProductName>     <lev:ProductName xmlns:lev="uri1">Product_6</lev:ProductName>     <lev:ProductName xmlns:lev="uri1">Product_8</lev:ProductName>   </lev:Products> </lev:ProductClass> 

Конструкция data() позволяет сформировать список значений, например требуется перечислить все значения ProductId для каждого класса продукта в элементе ProductClass:

select pc.ProductClassName as "@ProductClassName"     ,(select pid.ProductId as "data()"         from dbo.Product pid     where pid.ProductClassId = pc.ProductClassId         for xml path ('')) as "@ProductIds"     ,(select p.ProductId as "@ProductId", p.ProductName as '@ProductName'         from dbo.Product p     where p.ProductClassId = pc.ProductClassId         for xml path('Product'), type) as "Products" from dbo.ProductClass pc     for xml path('ProductClass'), type 

<ProductClass ProductClassName="Class_1" ProductIds="1 4 7 10">   <Products>     <Product ProductId="1" ProductName="Product_1" />     <Product ProductId="4" ProductName="Product_4" />     <Product ProductId="7" ProductName="Product_7" />     <Product ProductId="10" ProductName="Product_10" />   </Products> </ProductClass> <ProductClass ProductClassName="Class_2" ProductIds="2 5 9">   <Products>     <Product ProductId="2" ProductName="Product_2" />     <Product ProductId="5" ProductName="Product_5" />     <Product ProductId="9" ProductName="Product_9" />   </Products> </ProductClass> <ProductClass ProductClassName="Class_3" ProductIds="3 6 8">   <Products>     <Product ProductId="3" ProductName="Product_3" />     <Product ProductId="6" ProductName="Product_6" />     <Product ProductId="8" ProductName="Product_8" />   </Products> </ProductClass> 

Больше информации

ссылка на оригинал статьи https://habr.com/ru/post/506942/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *