Поделиться через


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

Область применения:SQL Server

В XQuery можно использовать прямые и вычисляемые конструкторы для создания структур XML в запросе.

Примечание.

Нет разницы между прямыми и вычисляемых конструкторами.

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

При использовании конструкторов 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 AS 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 AS 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 AS XML;
SET @x = '<root>5</root>';

DECLARE @y AS XML;
SET @y = (SELECT @x.query('
                           <NewRoot>
                             <e> { data(/root) } </e>
                           </NewRoot>'));

SELECT @y;

Ниже приведен результат:

<NewRoot>
  <e>5</e>
</NewRoot>

Если в качестве части текста желательно использовать фигурные скобки вместо токенов переключения контекста, то последние можно изолировать с помощью фигурных скобок «}}» или «{{», как показано в следующем примере:

DECLARE @x AS XML;
SET @x = '<root>5</root>';

DECLARE @y AS 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 AS 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 AS 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 AS 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 AS XML;
SET @x = '<root>5</root>';

DECLARE @y AS 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 AS 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 AS XML;
    SET @x = '<x>5</x>';
    
    SELECT @x.query('<a attr="{/x}{/x}"/>');
    
  • Разнородные последовательности не поддерживаются. Любая попытка назначить разнородную последовательность в качестве значения атрибута возвращает ошибку, как показано в следующем примере. В этом примере разнородная последовательность, строка "Item" и элемент <x>указываются в качестве значения атрибута:

    DECLARE @x AS 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 AS XML;
SET @x = '<x>5</x>';

SELECT @x.query('
  <a xmlns="a">
    <b xmlns=""/>
  </a>');

Ниже приведен результат:

<a xmlns="a">
  <b xmlns="" />
</a>

Пространству имен можно назначить префикс. Префикс указывается в построении элемента <a>.

DECLARE @x AS 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 AS 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 AS 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 replaced by space.
DECLARE @x AS 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>

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

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

  • элемент
  • атрибут
  • text

В узлах элементов и атрибутов эти ключевые слова используются перед именем узла и перед заключенным в фигурные скобки выражением, которое формирует содержимое этого узла. В следующем примере вы создаете этот XML-код:

<root>
  <ProductModel PID="5">Some text <summary>Some Summary</summary></ProductModel>
</root>

Ниже приведен запрос, использующий вычисляемые конструкторы, создающие XML:

DECLARE @x AS XML;
SET @x = '';

SELECT @x.query('element root
               {
                  element ProductModel
     {
attribute PID { 5 },
text{"Some text "},
    element summary { "Some Summary" }
 }
               } ');

Выражение запроса может быть задано выражением, формирующим содержимое узла.

DECLARE @x AS 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:

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