Правила использования методов типа данных XML

Применимо к:База данныхSQL Server Azure SQL Управляемый экземпляр SQL Azure

В этом разделе описываются правила использования методов типа данных xml.

Инструкция PRINT

Методы типа данных xml не могут быть использованы в инструкции PRINT, как показано в следующем примере. Методы типа данных xml обрабатываются как подзапросы, а подзапросы в инструкции PRINT не разрешены. В результате следующий пример возвращает ошибку:

DECLARE @x XML
SET @x = '<root>Hello</root>'
PRINT @x.value('/root[1]', 'varchar(20)') -- will not work because this is treated as a subquery (select top 1 col from table)

В качестве решения можно вначале присвоить результат метода value() переменной типа xml, а потом использовать переменную в запросе.

DECLARE @x XML
DECLARE @c VARCHAR(max)
SET @x = '<root>Hello</root>'
SET @c = @x.value('/root[1]', 'VARCHAR(11)')
PRINT @c

Предложение GROUP BY

Методы типа данных xml интерпретируются внутри как подзапросы. Так как предложение GROUP BY требует скалярного выражения и не принимает статистических вычислений или подзапросов, нельзя указывать методы типа данных xml в предложении GROUP BY. Решением является вызов пользовательской функции, которая использует методы XML.

Ведение отчетов об ошибках

При сообщении об ошибках методы типа данных xml вызывают ошибки в следующем формате:

Msg errorNumber, Level levelNumber, State stateNumber:
XQuery [database.table.method]: description_of_error

Пример:

Msg 2396, Level 16, State 1:
XQuery [xmldb_test.xmlcol.query()]: Attribute may not appear outside of an element

Одноэлементные проверки

Шаги определения расположения данных, параметры функций и операторы, которым нужны единственные значения, возвращают ошибку, если компилятор не может определить, будет ли в период выполнения гарантирована единственность элемента. Эта проблема часто возникает при работе с нетипизированными данными. Например, при уточняющем запросе атрибута необходима информация о единственном родительском элементе. Указать порядковый номер, выбирающий единственный родительский узел, недостаточно. Чтобы извлечь значения атрибутов при обработке комбинации node()-value() , указание порядкового номера, возможно, не потребуется. Это показано в следующем примере.

Пример Известный единственный экземпляр

В этом примере метод nodes() создает отдельную строку для каждого элемента <book>. Метод value() , выполняемый для узла <book>, извлекает значение @genre, которое, будучи атрибутом, является единственным.

SELECT nref.value('@genre', 'VARCHAR(max)') LastName
FROM T CROSS APPLY xCol.nodes('//book') AS R(nref)

Чтобы проверить типы типизированного XML, используется XML-схема. Если в XML-схеме узел указан как единственный, компилятор это учитывает, и ошибки не возникают. В противном случае необходим порядковый номер, выбирающий единственный узел. В частности, использование оси потомков или "я" (//), например в /book//title, теряет одноэлементный вывод кратности для <title> элемента, даже если схема XML указывает, что это так. Поэтому следует переписать его как (/book//title)[1].

Важно знать о различии между выражениями //first-name[1] и (//first-name)[1] при проверке типов. Первое из них возвращает последовательность узлов <first-name>, в которой каждый узел является самым левым узлом <first-name> среди узлов с общим родителем. Второе возвращает первый единственный узел <first-name> в порядке документа в экземпляре XML.

Пример Использование метода value()

Следующий запрос данных из нетипизированного столбца XML приводит к статической ошибке компиляции. Это объясняется тем, что метод value() ожидает в качестве первого аргумента единственный узел, а компилятор не может определить, будет ли в период выполнения существовать только один узел <last-name>:

SELECT xCol.value('//author/last-name', 'NVARCHAR(50)') LastName
FROM T

Вот решение этой проблемы, которое возможно как вариант:

SELECT xCol.value('//author/last-name[1]', 'NVARCHAR(50)') LastName
FROM T

Однако это решение не устраняет ошибку, потому что в каждом экземпляре XML могут содержаться несколько узлов <author>. Следующий вариант работает:

SELECT xCol.value('(//author/last-name/text())[1]', 'NVARCHAR(50)') LastName
FROM T

Этот запрос возвращает значение первого элемента <last-name> в каждом из экземпляров XML.

См. также: