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


Рекомендации по использованию методов типа данных XML

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

В этой статье описаны рекомендации по использованию методов типа данных 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

Замечание

Синтаксический анализ ошибок, вызванных средством синтаксического анализа XQuery (например, синтаксической ошибкой в XML- методе типа данных XML), прерывает активную транзакцию независимо от XACT_ABORT параметра текущего сеанса.

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

Действия по расположению, параметры функции и операторы, требующие одноэлементных значений, возвращают ошибку, если компилятор не может определить, гарантируется ли одноэлемент во время выполнения. Эта проблема часто возникает при работе с нетипизированными данными. Например, при уточняющем запросе атрибута необходима информация о единственном родительском элементе. Указать порядковый номер, выбирающий единственный родительский узел, недостаточно. Вычисление сочетания 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-столбцу приводит к статической ошибке компиляции. Это связано с тем, что значение() ожидает однотонный узел в качестве первого аргумента, и компилятор не может определить, будет ли выполняться только один <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.