Построение XML (XQuery)
Применимо к:SQL Server
В XQuery можно использовать прямые и вычисляемые конструкторы для создания структур XML в запросе.
Заметка
Нет разницы между прямыми и вычисляемых конструкторами.
Использование конструкторов Direct
При использовании конструкторов direct в построении XML указывается синтаксис, подобный XML. В следующих примерах иллюстрируется построение XML с помощью конструкторов direct.
Построение элементов
В используемой нотации XML можно создавать элементы. В следующем примере используется выражение конструктора прямых элементов и создается <элемент ProductModel> . Созданный элемент содержит три дочерних элемента.
Текстовый узел.
Два узла элемента, <сводка> и <компоненты>.
Элемент <Summary> имеет один дочерний текстовый узел, значение которого — "Некоторые описания".
Элемент <Features> содержит три дочерних элемента, <Color>, <Weight> и <Warranty>. Каждый из этих узлов имеет по одному текстовому дочернему элементу и значения «Red», «25» и «2 years parts and labor» соответственно.
declare @x xml;
set @x='';
select @x.query('<ProductModel ProductModelID="111">;
This is product model catalog description.
<Summary>Some description</Summary>
<Features>
<Color>Red</Color>
<Weight>25</Weight>
<Warranty>2 years parts and labor</Warranty>
</Features></ProductModel>')
Результирующий XML-документ:
<ProductModel ProductModelID="111">
This is product model catalog description.
<Summary>Some description</Summary>
<Features>
<Color>Red</Color>
<Weight>25</Weight>
<Warranty>2 years parts and labor</Warranty>
</Features>
</ProductModel>
Хотя показанное в данном примере построение элементов из неизменных выражений полезно само по себе, настоящая мощь этой функции языка XQuery раскрывается при построении XML-документов с динамическим извлечением данных из базы данных. Для указания выражений запроса можно использовать фигурные скобки. В результирующем XML-документе выражение заменяется своим значением. Например, следующий запрос создает <NewRoot
> элемент с одним дочерним элементом ().<e
> Значение элемента <e
> вычисляется путем указания выражения пути внутри фигурных скобков ("{... }").
DECLARE @x xml;
SET @x='<root>5</root>';
SELECT @x.query('<NewRoot><e> { /root } </e></NewRoot>');
Фигурные скобки выступают в качестве токенов переключения контекста и переключают запрос с построения XML на оценку запроса. В данном случае производится оценка выражения пути XQuery внутри фигурных скобок, /root
, и вместо выражения подставляются результаты.
Результат:
<NewRoot>
<e>
<root>5</root>
</e>
</NewRoot>
Следующий запрос похож на предыдущий. Однако выражение в фигурных скобках указывает функцию data() для получения атомарного значения>root
< элемента и назначает его созданному элементу. <e
>
DECLARE @x xml;
SET @x='<root>5</root>';
DECLARE @y xml;
SET @y = (SELECT @x.query('
<NewRoot>
<e> { data(/root) } </e>
</NewRoot>' ));
SELECT @y;
Результат:
<NewRoot>
<e>5</e>
</NewRoot>
Если в качестве части текста желательно использовать фигурные скобки вместо токенов переключения контекста, то последние можно изолировать с помощью фигурных скобок «}}» или «{{», как показано в следующем примере:
DECLARE @x xml;
SET @x='<root>5</root>';
DECLARE @y xml;
SET @y = (SELECT @x.query('
<NewRoot> Hello, I can use {{ and }} as part of my text</NewRoot>'));
SELECT @y;
Результат:
<NewRoot> Hello, I can use { and } as part of my text</NewRoot>
Следующий запрос — еще один пример построения элементов с помощью прямого конструктора элементов. Кроме того, значение <FirstLocation
> элемента получается путем выполнения выражения в фигурных скобках. Выражение запроса возвращает шаги изготовления к первому цеху из столбца Instructions таблицы Production.ProductModel.
SELECT Instructions.query('
declare namespace AWMI="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelManuInstructions";
<FirstLocation>
{ /AWMI:root/AWMI:Location[1]/AWMI:step }
</FirstLocation>
') as Result
FROM Production.ProductModel
WHERE ProductModelID=7;
Результат:
<FirstLocation>
<AWMI:step xmlns:AWMI="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelManuInstructions">
Insert <AWMI:material>aluminum sheet MS-2341</AWMI:material> into the <AWMI:tool>T-85A framing tool</AWMI:tool>.
</AWMI:step>
<AWMI:step xmlns:AWMI="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelManuInstructions">
Attach <AWMI:tool>Trim Jig TJ-26</AWMI:tool> to the upper and lower right corners of the aluminum sheet.
</AWMI:step>
...
</FirstLocation>
Содержимое элемента в конструкции XML
В следующем примере показано использование выражений при построении содержимого элемента с помощью прямого конструктора элементов. В этом примере прямой конструктор элементов указывает одно выражение. Для этого выражения в результирующем XML-документе создается один текстовый узел.
declare @x xml;
set @x='
<root>
<step>This is step 1</step>
<step>This is step 2</step>
<step>This is step 3</step>
</root>';
select @x.query('
<result>
{ for $i in /root[1]/step
return string($i)
}
</result>');
Последовательность элементарного значения, рассчитанная при оценке выражения, добавляется в текстовый узел с пробелом между соседними элементарными значениями, как это показано в результате. Построенный элемент содержит один дочерний элемент. Это текстовый узел, который хранит значение, показанное в результате.
<result>This is step 1 This is step 2 This is step 3</result>
Если вместо одного выражения задать три отдельных выражения, формирующих три текстовых узла, в результирующем XML-документе происходит объединение соседних текстовых узлов в один узел методом сцепления.
declare @x xml;
set @x='
<root>
<step>This is step 1</step>
<step>This is step 2</step>
<step>This is step 3</step>
</root>';
select @x.query('
<result>
{ string(/root[1]/step[1]) }
{ string(/root[1]/step[2]) }
{ string(/root[1]/step[3]) }
</result>');
Построенный узел элементов содержит один дочерний элемент. Это текстовый узел, который хранит значение, показанное в результате.
<result>This is step 1This is step 2This is step 3</result>
Построение атрибутов
При построении элемента с использованием прямого конструктора элементов можно также задать атрибуты элемента с помощью синтаксиса, подобного XML, как показано в этом примере:
declare @x xml;
set @x='';
select @x.query('<ProductModel ProductModelID="111">;
This is product model catalog description.
<Summary>Some description</Summary>
</ProductModel>')
Результирующий XML-документ:
<ProductModel ProductModelID="111">
This is product model catalog description.
<Summary>Some description</Summary>
</ProductModel>
Созданный элемент <ProductModel
> имеет атрибут ProductModelID и следующие дочерние узлы:
Текстовый узел
This is product model catalog description.
Узел элемента, <
Summary
>. Этот узел содержит один дочерний текстовый узел,Some description
.
При построении атрибута его значение можно указать с помощью выражения в фигурных скобках. В этом случае результат выражения возвращается в качестве значения атрибута.
В следующем примере функция data() не является строго обязательной. Так как значение выражения присваивается атрибуту, данные() неявно применяются для получения типизированного значения указанного выражения.
DECLARE @x xml;
SET @x='<root>5</root>';
DECLARE @y xml;
SET @y = (SELECT @x.query('<NewRoot attr="{ data(/root) }" ></NewRoot>'));
SELECT @y;
Результат:
<NewRoot attr="5" />
Ниже приведен еще один пример, в котором указаны выражения для построения атрибутов LocationID и SetupHrs. Оценка этих выражений производится по XML-документу, содержащемуся в столбце Instruction. Типизированное значение выражения присваивается атрибутам.
SELECT Instructions.query('
declare namespace AWMI="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelManuInstructions";
<FirstLocation
LocationID="{ (/AWMI:root/AWMI:Location[1]/@LocationID)[1] }"
SetupHrs = "{ (/AWMI:root/AWMI:Location[1]/@SetupHours)[1] }" >
{ /AWMI:root/AWMI:Location[1]/AWMI:step }
</FirstLocation>
') as Result
FROM Production.ProductModel
where ProductModelID=7;
Частичный результат:
<FirstLocation LocationID="10" SetupHours="0.5" >
<AWMI:step ...
</AWMI:step>
...
</FirstLocation>
Ограничения реализации
Существуют следующие ограничения:
Не поддерживается использование выражений с несколькими атрибутами или смешанными атрибутами (строка и выражение языка XQuery). Например, как показано в следующем запросе, создается XML-документ, в котором
Item
является константой, а значение5
получено при оценке выражения запроса:<a attr="Item 5" />
Следующий запрос возвращает ошибку, потому что в нем строка константы смешана с выражением ({/x}), а это не поддерживается:
DECLARE @x xml SET @x ='<x>5</x>' SELECT @x.query( '<a attr="Item {/x}"/>' )
В этом случае имеются варианты, перечисленные ниже.
Сформировать значение атрибута путем объединения двух элементарных значений. Эти элементарные значения включены последовательно в значение атрибута с пробелом между элементарными значениями:
SELECT @x.query( '<a attr="{''Item'', data(/x)}"/>' )
Результат:
<a attr="Item 5" />
Используйте функцию сцепления для объединения двух строковых аргументов в итоговое значение атрибута:
SELECT @x.query( '<a attr="{concat(''Item'', /x[1])}"/>' )
В этом случае между двумя строковыми значениями пробелы отсутствуют. Если требуется добавить пробелы между двумя значениями, необходимо задать их явно.
Результат:
<a attr="Item5" />
Не поддерживается использование нескольких выражений в качестве значения атрибута. Например, следующий запрос возвращает ошибку:
DECLARE @x xml SET @x ='<x>5</x>' SELECT @x.query( '<a attr="{/x}{/x}"/>' )
Не поддерживаются разнородные последовательности. При попытке присвоения значению атрибута разнородной последовательности произойдет ошибка, как показано в следующем примере. В этом примере разнородная последовательность, строка "Item" и элемент <
x
>указываются в качестве значения атрибута:DECLARE @x xml SET @x ='<x>5</x>' select @x.query( '<a attr="{''Item'', /x }" />')
При применении функции data() запрос работает, так как он получает атомарное значение выражения,
/x
которое объединяется со строкой. Ниже приведена последовательность элементарных значений:SELECT @x.query( '<a attr="{''Item'', data(/x)}"/>' )
Результат:
<a attr="Item 5" />
Узел атрибутов принудительно упорядочивается во время сериализации, а не при статической проверке типов. Например, следующий запрос завершится ошибкой, потому что запрос пытается добавить атрибут после узла без атрибутов.
select convert(xml, '').query(' element x { attribute att { "pass" }, element y { "Element text" }, attribute att2 { "fail" } } ') go
Результат этого запроса будет таким:
XML well-formedness check: Attribute cannot appear outside of element declaration. Rewrite your XQuery so it returns well-formed XML.
Добавление пространств имен
При построении XML-документа с помощью прямых конструкторов имена создаваемого элемента и атрибута можно определить с помощью префикса пространства имен. Префикс можно связать с пространством имен следующими способами:
с помощью атрибута объявления пространства имен;
с помощью предложения WITH XMLNAMESPACES;
в прологе запроса на языке XQuery.
Добавление пространств имен с помощью атрибута объявления пространства имен
В следующем примере используется атрибут объявления пространства имен в построении элемента <a
> для объявления пространства имен по умолчанию. Построение дочернего элемента <b
> отменяет объявление пространства имен по умолчанию, объявленного в родительском элементе.
declare @x xml
set @x ='<x>5</x>'
select @x.query( '
<a xmlns="a">
<b xmlns=""/>
</a>' )
Результат:
<a xmlns="a">
<b xmlns="" />
</a>
Пространству имен можно назначить префикс. Префикс указывается в построении элемента <a
>.
declare @x xml
set @x ='<x>5</x>'
select @x.query( '
<x:a xmlns:x="a">
<b/>
</x:a>' )
Результат:
<x:a xmlns:x="a">
<b />
</x:a>
Можно отменить объявление пространства имен, используемого по умолчанию в конструкции XML, однако нельзя отменить объявление префикса пространства имен. Следующий запрос возвращает ошибку, так как нельзя отменить объявление префикса, как указано в построении элемента <b
>.
declare @x xml
set @x ='<x>5</x>'
select @x.query( '
<x:a xmlns:x="a">
<b xmlns:x=""/>
</x:a>' )
Новое построенное пространство имен доступно для использования внутри запроса. Например, следующий запрос объявляет пространство имен при создании элемента <FirstLocation
>и задает префикс в выражениях для значений атрибута LocationID и SetupHrs.
SELECT Instructions.query('
<FirstLocation xmlns:AWMI="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelManuInstructions"
LocationID="{ (/AWMI:root/AWMI:Location[1]/@LocationID)[1] }"
SetupHrs = "{ (/AWMI:root/AWMI:Location[1]/@SetupHours)[1] }" >
{ /AWMI:root/AWMI:Location[1]/AWMI:step }
</FirstLocation>
') as Result
FROM Production.ProductModel
where ProductModelID=7
Обратите внимание, что при создании префикса пространства имен этим способом произойдет замена любых ранее существовавших объявлений пространства имен для этого префикса. Например, объявление пространства имен в AWMI="https://someURI"
прологе запроса переопределяется объявлением пространства имен в элементеFirstLocation
<>.
SELECT Instructions.query('
declare namespace AWMI="https://someURI";
<FirstLocation xmlns:AWMI="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelManuInstructions"
LocationID="{ (/AWMI:root/AWMI:Location[1]/@LocationID)[1] }"
SetupHrs = "{ (/AWMI:root/AWMI:Location[1]/@SetupHours)[1] }" >
{ /AWMI:root/AWMI:Location[1]/AWMI:step }
</FirstLocation>
') as Result
FROM Production.ProductModel
where ProductModelID=7
Использование пролога для добавления пространств имен
В следующем примере показан порядок добавления пространств имен в построенный XML-документ. Пространство имен по умолчанию объявляется в прологе запроса.
declare @x xml
set @x ='<x>5</x>'
select @x.query( '
declare default element namespace "a";
<a><b xmlns=""/></a>' )
Обратите внимание, что в построении элемента <b
>атрибут объявления пространства имен указывается пустой строкой в качестве значения. Это отменяет объявление пространства имен, произведенное по умолчанию в родительском элементе.
Результат:
<a xmlns="a">
<b xmlns="" />
</a>
Построение XML и управление пробелами
Содержимое элемента в конструкции XML может содержать пробелы. Эти символы обрабатываются следующим образом.
Символы пробелов в URI пространства имен рассматриваются как XSD-тип anyURI. В частности, такая обработка заключается в следующем.
Все начальные и конечные пробелы удаляются.
Внутренние последовательности пробельных символов заменяются одиночными пробелами.
Символы перехода на новую строку внутри содержимого атрибутов заменяются пробелами. Все остальные пробелы остаются неизменными.
Пробелы внутри элементов не изменяются.
В следующем примере показана обработка пробельных символов в конструкции XML:
-- line feed is repaced by space.
declare @x xml
set @x=''
select @x.query('
declare namespace myNS=" https://
abc/
xyz
";
<test attr=" my
test attr
value " >
<a>
This is a
test
</a>
</test>
') as XML_Result
Результат:
-- result
<test attr="<test attr=" my test attr value "><a>
This is a
test
</a></test>
"><a>
This is a
test
</a></test>
Другие прямые конструкторы XML
В конструкторах для обработки инструкций и комментариев XML используется тот же синтаксис, что и в соответствующих конструкциях XML. Имеется также поддержка вычисляемых конструкторов для текстовых узлов, но они используются в основном в языке XML DML для построения текстовых узлов.
Примечание. Пример использования явного конструктора текстового узла см. в конкретном примере вставки (XML DML).
В следующем запросе созданный XML-документ содержит элемент, два атрибута, комментарий и инструкцию по обработке. Обратите внимание, что перед созданием последовательности используется <FirstLocation
>запятая.
SELECT Instructions.query('
declare namespace AWMI="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelManuInstructions";
<?myProcessingInstr abc="value" ?>,
<FirstLocation
WorkCtrID = "{ (/AWMI:root/AWMI:Location[1]/@LocationID)[1] }"
SetupHrs = "{ (/AWMI:root/AWMI:Location[1]/@SetupHours)[1] }" >
<!-- some comment -->
<?myPI some processing instructions ?>
{ (/AWMI:root/AWMI:Location[1]/AWMI:step) }
</FirstLocation>
') as Result
FROM Production.ProductModel
where ProductModelID=7;
Частичный результат:
<?myProcessingInstr abc="value" ?>
<FirstLocation WorkCtrID="10" SetupHrs="0.5">
<!-- some comment -->
<?myPI some processing instructions ?>
<AWMI:step xmlns:AWMI="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelManuInstructions">I
nsert <AWMI:material>aluminum sheet MS-2341</AWMI:material> into the <AWMI:tool>T-85A framing tool</AWMI:tool>.
</AWMI:step>
...
</FirstLocation>
Использование вычисляемых конструкторов
. В данном случае указываются ключевые слова, которые идентифицируют тип узла, подлежащий построению. Поддерживается использование только следующих ключевых слов:
элемент
атрибут
текст
В узлах элементов и атрибутов эти ключевые слова используются перед именем узла и перед заключенным в фигурные скобки выражением, которое формирует содержимое этого узла. В приведенном ниже примере создается этот XML-документ:
<root>
<ProductModel PID="5">Some text <summary>Some Summary</summary></ProductModel>
</root>
Этот запрос использует вычисляемые конструкторы для формирования XML-документа:
declare @x xml
set @x=''
select @x.query('element root
{
element ProductModel
{
attribute PID { 5 },
text{"Some text "},
element summary { "Some Summary" }
}
} ')
Выражение запроса может быть задано выражением, формирующим содержимое узла.
declare @x xml
set @x='<a attr="5"><b>some summary</b></a>'
select @x.query('element root
{
element ProductModel
{
attribute PID { /a/@attr },
text{"Some text "},
element summary { /a/b }
}
} ')
Обратите внимание, что вычисляемые конструкторы элементов и атрибутов, определенные в спецификации языка XQuery, позволяют вычислять имена узлов. При использовании прямых конструкторов в SQL Server имена узлов, такие как элемент и атрибут, должны быть указаны в виде константных литералов. Таким образом, различий между прямыми конструкторами и вычисляемыми конструкторами элементов и атрибутов нет.
В следующем примере содержимое созданных узлов получено из XML-инструкций по производству, хранящихся в столбце "Инструкции" типа данных XML в таблице ProductModel.
SELECT Instructions.query('
declare namespace AWMI="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelManuInstructions";
element FirstLocation
{
attribute LocationID { (/AWMI:root/AWMI:Location[1]/@LocationID)[1] },
element AllTheSteps { /AWMI:root/AWMI:Location[1]/AWMI:step }
}
') as Result
FROM Production.ProductModel
where ProductModelID=7
Частичный результат:
<FirstLocation LocationID="10">
<AllTheSteps>
<AWMI:step> ... </AWMI:step>
<AWMI:step> ... </AWMI:step>
...
</AllTheSteps>
</FirstLocation>
Дополнительные ограничения реализации
Вычисляемые конструкторы атрибутов не могут использоваться для объявления нового пространства имен. Кроме того, следующие вычисляемые конструкторы не поддерживаются в SQL Server:
вычисляемые конструкторы узлов документов;
вычисляемые конструкторы инструкций обработки;
вычисляемые конструкторы комментариев.