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


Метод 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 AdventureWorks;
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-данных в базе данных AdventureWorks.

В следующем примере метод 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