Небольшая заметка по формированию 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/
Добавить комментарий