Построение XML (XQuery)

Для построения структур XML внутри запросов на языке XQuery можно использовать конструкторы direct и computed.

Примечание

Между конструкторами direct и computed нет никаких различий.

Использование конструкторов 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>

Можно отменить объявление пространства имен, используемого по умолчанию в конструкции 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 не поддерживаются следующие вычисляемые конструкторы:

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

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

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

См. также

Основные понятия

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