Построение XML (XQuery)
Для построения структур XML внутри запросов на языке XQuery можно использовать конструкторы direct и computed.
Примечание |
---|
В версиях ниже SQL Server расчет имен узлов при использовании вычисляемых конструкторов не поддерживается. Поэтому между конструкторами direct и computed нет никаких различий. |
В версиях до SQL Server поддерживаются только режим конструктора, чередование и политика XMLSpace (политика чередования пограничных пробелов).
Использование конструкторов Direct
При использовании конструкторов direct в построении XML указывается синтаксис, подобный XML. В следующих примерах иллюстрируется построение XML с помощью конструкторов direct.
Построение элементов
В используемой нотации XML можно создавать элементы. В следующем примере используется выражение прямого конструктора элементов и создается элемент <ProductModel>. Созданный элемент содержит три дочерних элемента.
Текстовый узел.
Два узла элементов, <Summary> и <Features>.
Элемент <Summary> имеет один дочерний элемент, а именно: текстовый узел со значением «Some description».
Элемент <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() не обязательно. Поскольку значение выражения присваивается атрибуту, функция 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" />
Для объединения двух строковых аргументов в результирующее значение атрибута используется функция concat:
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 />
</a>' )
Результирующий набор:
<a xmlns="a">
<b />
</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>
Начиная с версии SQL Server 2005 можно отменить объявление пространства имен, используемого по умолчанию в конструкции 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="http://someURI" в прологе запроса будет заменено объявлением пространства имен в элементе <FirstLocation>.
SELECT Instructions.query('
declare namespace AWMI="http://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 /></a>' )
Обратите внимание на то, что в конструкции элемента <b> атрибут объявления пространства имен указан с пустой строкой в качестве значения. Это отменяет объявление пространства имен, произведенное по умолчанию в родительском элементе.
This is the result:
<a xmlns="a">
<b />
</a>
Построение XML и управление пробелами
Содержимое элемента в конструкции XML может содержать пробелы. Эти символы обрабатываются следующим образом.
Пробельные символы в URI-кодах пространства имен обрабатываются как XSD-тип anyURI. В частности, такая обработка заключается в следующем.
Все начальные и конечные пробелы удаляются.
Внутренние последовательности пробельных символов заменяются одиночными пробелами.
Символы перехода на новую строку внутри содержимого атрибутов заменяются пробелами. Все остальные пробелы остаются неизменными.
Пробелы внутри элементов не изменяются.
В следующем примере показана обработка пробельных символов в конструкции XML:
-- line feed is repaced by space.
declare @x xml
set @x=''
select @x.query('
declare namespace myNS=" http://
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 для построения текстовых узлов.
Примечание Конкретный пример использования явного конструктора текстового узла см. в разделе insert (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>
Использование вычисляемых конструкторов
. В данном случае указываются ключевые слова, которые идентифицируют тип узла, подлежащий построению. Поддерживается использование только следующих ключевых слов:
element
attribute
text
В узлах элементов и атрибутов эти ключевые слова используются перед именем узла и перед заключенным в фигурные скобки выражением, которое формирует содержимое этого узла. В приведенном ниже примере создается этот 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 имена узлов, такие как element и attribute, должны быть указаны в виде постоянных литералов. Таким образом, различий между прямыми конструкторами и вычисляемыми конструкторами элементов и атрибутов нет.
В следующем примере содержимое конструируемых узлов извлекается из инструкций по производству в формате XML, которые хранятся в столбце Instructions типа 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 не поддерживаются следующие вычисляемые конструкторы:
вычисляемые конструкторы узлов документов;
вычисляемые конструкторы инструкций обработки;
вычисляемые конструкторы комментариев.