Метод nodes() (тип данных xml)

Метод nodes() незаменим в тех случаях, когда экземпляр типа данных xml необходимо разделить на набор реляционных данных. Он позволяет идентифицировать узлы, которые будут ставиться в соответствие новой строке.

У каждого экземпляра типа данных xml имеется неявно заданный контекстный узел. Для сохраняемого в столбце или переменной экземпляра XML таким узлом является узел документов. Узел документа является неявным узлом, находящимся на верхнем уровне каждого экземпляра типа данных xml.

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

Из этих наборов строк могут извлекаться несколько значений. Например, метод value() может быть применен к набору строк, возвращенному методом nodes(), для извлечения нескольких значений из исходного экземпляра XML. Обратите внимание на то, что метод value(), примененный к экземпляру XML, возвращает только одно значение.

Синтаксис

nodes (XQuery) as Table(Column)

Аргументы

  • XQuery
    Строковый литерал, выражение XQuery. Если выражение запроса конструирует узлы, эти сконструированные узлы представлены в результирующем наборе строк. Если результатом выражения запроса является пустая последовательность, набор строк будет пустым. Если статическим результатом выражения запроса является последовательность, которая вместо узлов содержит атомарные значения, возникает статическая ошибка.

  • Table(Column)
    Имя таблицы и имя столбца для результирующего набора строк.

Замечания

В качестве примера предположим, что имеется следующая таблица:

T (ProductModelID int, Instructions xml)

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

<root>
  <Location LocationID="10"...>
     <step>...</step>
     <step>...</step>
      ...
  </Location>
  <Location LocationID="20" ...>
       ...
  </Location>
  <Location LocationID="30" ...>
       ...
  </Location>
</root>

Вызов метода nodes() с выражением запроса /root/Location возвращает набор из трех строк, каждая из которых содержит логическую копию исходного XML-документа с контекстным элементом, присвоенным одному из узлов <Location>:

Product
ModelID      Instructions
----------------------------------
1       <root>
             <Location LocationID="20" ... />
             <Location LocationID="30" .../></root>
1      <root><Location LocationID="10" ... />
             
             <Location LocationID="30" .../></root>
1      <root><Location LocationID="10" ... />
             <Location LocationID="20" ... />
             </root>

Затем для набора строк можно выполнять запросы при помощи методов типа данных xml. Следующий запрос извлекает поддерево элемента контекста для каждой сформированной строки:

SELECT T2.Loc.query('.')
FROM   T
CROSS APPLY Instructions.nodes('/root/Location') as T2(Loc) 

Результирующий набор:

ProductModelID  Instructions
----------------------------------
1        <Location LocationID="10" ... />
1        <Location LocationID="20" ... />
1        <Location LocationID="30" .../>

Обратите внимание на то, что в возвращаемом наборе строк сохраняется информация о типе. Методы типа данных xml (query(), value(), exist() и nodes()) могут применяться к результату, возвращенному методом nodes(). Однако для изменения экземпляра XML-метод modify() применять нельзя.

Кроме того, контекстный узел в наборе строк не может быть материализован. Это означает, что он не может использоваться в инструкции SELECT. Однако он может использоваться в выражениях IS NULL и COUNT(*).

Сценарии применения метода nodes() и инструкции OPENXML (Transact-SQL) совпадают. Тем самым обеспечивается представление набора строк XML. Однако при использовании метода nodes() на таблице, содержащей несколько строк XML-документов, нельзя использовать курсоры.

Обратите внимание на то, что набор строк, возвращаемый методом nodes(), является неименованным набором строк. Поэтому он должен быть именован при помощи механизма псевдонимов.

Функция nodes() не может быть применена непосредственно к результатам пользовательской функции. Для использования функции nodes() с результатом скалярной пользовательской функции можно или назначить результат скалярной пользовательской функции переменной, или использовать производную таблицу для назначения псевдонима столбца значению, возвращаемому пользовательской функцией, а затем использовать CROSS APPLY для выбора из псевдонима.

В следующем примере показан один из способов использования инструкции CROSS APPLY для выборки из результата пользовательской функции.

USE AdventureWorks2008R2;
GO

CREATE FUNCTION XTest()
RETURNS xml
AS
BEGIN
RETURN '<document/>';
END;
GO


SELECT A2.B.query('.')
FROM
(SELECT dbo.XTest()) AS A1(X) 
CROSS APPLY X.nodes('.') A2(B);
GO

DROP FUNCTION XTest;
GO

Примеры

Применение метода nodes() к переменной типа данных xml

В этом примере имеется XML-документ с элементом верхнего уровня <Root> и тремя дочерними элементами <row>. Запрос использует метод nodes() для разделения контекстных узлов, по одному для каждого элемента <row>. Метод nodes() возвращает набор из трех строк. Каждая строка имеет логическую копию исходного XML, с контекстными узлами, идентифицирующими разные элементы <row> исходного документа.

Запрос возвращает контекстный узел из каждой строки:

DECLARE @x xml ;
SET @x='<Root>
    <row id="1"><name>Larry</name><oflw>some text</oflw></row>
    <row id="2"><name>moe</name></row>
    <row id="3" />
</Root>';
SELECT T.c.query('.') AS result
FROM   @x.nodes('/Root/row') T(c);
GO

Ниже показан результат. В этом примере метод запроса возвращает элемент контекста и его содержимое:

 <row id="1"><name>Larry</name><oflw>some text</oflw></row>
 <row id="2"><name>moe</name></row>
 <row id="3"/>

Применение родительского метода доступа на контекстных узлах возвращает элемент <Root> для всех трех узлов:

SELECT T.c.query('..') AS result
FROM   @x.nodes('/Root/row') T(c);
go

Результирующий набор:

<Root>
    <row id="1"><name>Larry</name><oflw>some text</oflw></row>
    <row id="2"><name>moe</name></row>
    <row id="3" />
</Root>
<Root>
    <row id="1"><name>Larry</name><oflw>some text</oflw></row>
    <row id="2"><name>moe</name></row>
    <row id="3" />
</Root>
<Root>
    <row id="1"><name>Larry</name><oflw>some text</oflw></row>
    <row id="2"><name>moe</name></row>
    <row id="3" />
</Root>

Задание метода nodes() для столбца типа данных xml

В этом примере использовано руководство по производству велосипедов, которое хранится в столбце Instructions типа xml в таблице ProductModel. Дополнительные сведения см. в разделе Представление типов данных XML в базе данных AdventureWorks2008R2.

В следующем примере метод nodes() применяется к столбцу Instructions типа xml в таблице ProductModel.

Метод nodes() устанавливает элементы <Location> как контекстные узлы, задавая путь /MI:root/MI:Location. Результирующий набор строк включает в себя логические копии исходного документа, одну для каждого узла <Location> в документе, с контекстным узлом, установленным в элемент <Location>. Таким образом, функция nodes() выдает набор контекстных узлов <Location>.

Метод query(), применяемый к этому набору строк, запрашивает self::node и возвращает элемент <Location> для каждой строки.

В этом примере запрос устанавливает каждый элемент <Location> как контекстный узел в руководстве по изготовлению определенной производственной модели. Эти контекстные узлы можно использовать для извлечения значений следующим образом:

  • Найти идентификаторы LocationID в каждом элементе <Location>

  • Получить шаги производства (дочерние элементы <step>) в каждом элементе <Location>

Этот запрос возвращает контекстный элемент, у которого в методе query() используется сокращенный синтаксис '.' для self::node().

Следует отметить следующее.

  • Метод nodes() применяется к столбцу Instructions и возвращает набор строк T (C). Этот набор строк содержит логические копии исходного руководства по изготовлению с /root/Location в качестве контекстного элемента.

  • Инструкция CROSS APPLY применяет метод nodes() к каждой строке в таблице Instructions и возвращает только строки, образующие результирующий набор.

    SELECT C.query('.') as result
    FROM Production.ProductModel
    CROSS APPLY Instructions.nodes('
    declare namespace MI="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelManuInstructions";
    /MI:root/MI:Location') as T(C)
    WHERE ProductModelID=7
    

    Промежуточный результат:

    <MI:Location LocationID="10"  ...>
       <MI:step ... />
          ...
    </MI:Location>
    <MI:Location LocationID="20"  ... >
        <MI:step ... />
          ...
    </MI:Location>
    ...
    

Применение метода nodes() к набору строк, возвращенному другим методом nodes()

В следующем коде из столбца Instructions таблицы ProductModel запрашиваются XML-документы, составляющие руководство по изготовлению. Запрос возвращает набор строк, содержащий идентификатор производственной модели, места производства и шаги производства.

Следует отметить следующее.

  • Метод nodes() применяется к столбцу Instructions и возвращает набор строк T1 (Locations), который содержит логические копии исходного руководства по изготовлению с /root/Location в качестве контекстного элемента.

  • Метод nodes() применяется к набору строк T1 (Locations) и возвращает набор строк T2 (steps). Этот набор строк содержит логические копии исходного руководства по изготовлению с /root/Location/step в качестве контекстного элемента.

SELECT ProductModelID, Locations.value('./@LocationID','int') as LocID,
steps.query('.') as Step       
FROM Production.ProductModel       
CROSS APPLY Instructions.nodes('       
declare namespace MI="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelManuInstructions";       
/MI:root/MI:Location') as T1(Locations)       
CROSS APPLY T1.Locations.nodes('       
declare namespace MI="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelManuInstructions";       
./MI:step ') as T2(steps)       
WHERE ProductModelID=7       
GO       

Результирующий набор:

ProductModelID LocID Step       
----------------------------       
7      10   <step ... />       
7      10   <step ... />       
...       
7      20   <step ... />       
7      20   <step ... />       
7      20   <step ... />       
...       

В запросе префикс MI объявляется два раза. Вместо этого можно воспользоваться WITH XMLNAMESPACES, чтобы объявить этот префикс один раз и использовать его в запросе:

WITH XMLNAMESPACES (
   'https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelManuInstructions'  AS MI)

SELECT ProductModelID, Locations.value('./@LocationID','int') as LocID,
steps.query('.') as Step       
FROM Production.ProductModel       
CROSS APPLY Instructions.nodes('       
/MI:root/MI:Location') as T1(Locations)       
CROSS APPLY T1.Locations.nodes('       
./MI:step ') as T2(steps)       
WHERE ProductModelID=7       
GO