Построение 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" />  
      
    • Используйте функцию 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 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>  
  

Использование вычисляемых конструкторов

. В данном случае указываются ключевые слова, которые идентифицируют тип узла, подлежащий построению. Поддерживается использование только следующих ключевых слов:

  • element

  • Атрибут

  • 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 имена узлов, такие как элемент и атрибут, должны быть указаны в виде константных литералов. Таким образом, различий между прямыми конструкторами и вычисляемыми конструкторами элементов и атрибутов нет.

В следующем примере содержимое созданных узлов получается из инструкций по производству 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 не поддерживаются следующие вычисляемые конструкторы:

  • вычисляемые конструкторы узлов документов;

  • вычисляемые конструкторы инструкций обработки;

  • вычисляемые конструкторы комментариев.

См. также:

Выражения языка XQuery