2013-07-19 5 views
12

Использует ли метод nodes() типа данных типа ввода данных в порядке документа?Поддерживает ли метод `nodes()` порядок документа?

Например, если есть данные, такие как:

declare @xml xml 
set @xml = '<Fruits><Apple /><Banana /><Orange /><Pear /></Fruits>' 

, которая запрашивается в качестве

select T.c.query('.') 
from @xml.nodes('/Fruits/*') T(c) 

будут элементы будут возвращены в порядке документа? Порядок строк, возвращаемых select, как известно, не определен, если пункт order by опущен. Это дело для select ... from ... .nodes(), или это исключительный?

Можно ли в некоторых обстоятельствах, чтобы получить непустой набор строк в выводе следующего запроса:

declare @xml xml 
set @xml = '<Data><Element OrderNo="1" /><Element OrderNo="2" />' 
    + '<Element OrderNo="3" /><Element OrderNo="4" />' 
    + '<Element OrderNo="5" /></Data>' 

select * from (
    select T.c.value('.', 'int') OrderNo1, 
     row_number() over (order by @@spid) OrderNo2 
    from @xml.nodes('/Data/Element/@OrderNo') T(c)) sq 
where OrderNo1 != OrderNo2 

ответ

19

Да, nodes() генерирует набор строк в порядке документа. Оператор, используемый в плане запроса для этого, - это Table Valued Function XML Reader.

Табличная функция XML-Reader вводит XML-BLOB в качестве параметра, а создает набор строк, представляющий узлы XML в порядке XML-документа. Другие входные параметры могут ограничивать узлы XML, возвращаемые подмножеству документа XML .

Но запрос без order by имеет неопределенный заказ, поэтому никаких гарантий нет.

Один из способов обойти это - использовать id, сгенерированный табличной функцией в row_number() over(), и использовать сгенерированное число в порядке.

select X.q 
from 
    (
    select T.c.query('.') as q, 
     row_number() over(order by T.c) as rn 
    from @xml.nodes('/Fruits/*') T(c) 
) as X 
order by X.rn 

Это не представляется возможным использовать T.c в order by непосредственно. Попытка, что даст вам

Msg 493, Level 16, State 1, Line 19
Колонка 'с', который был возвращен из узлов() метод не может быть использован непосредственно. Его можно использовать только с одним из четырех методов типа данных XML: exist(), nodes(), query() и value() или в IS NULL и IS NOT NULL.

Ошибка не упоминает, что она должна работать с row_number, но это так, и это может быть ошибкой, которая может быть исправлена, поэтому код выше не будет работать. Но до SQL Server 2012 он работает отлично.

Способ получения гарантированного заказа без использования недокументированного использования row_number заключается в использовании таблицы чисел, в которой вы извлекаете узлы по положению.

select T.c.query('.') as q 
from Numbers as N 
    cross apply @xml.nodes('/Fruits/*[sql:column("N.Number")]') as T(c) 
where N.Number between 1 and @xml.value('count(/Fruits/*)', 'int') 
order by N.Number 
+2

Хотя я оставил это сообщение (потому что он содержит лучший сбор информации, который я видел по этой теме), это определенная дыра в реализации Microsoft SQL. Oracle, DB2 и PostgreSQL (как минимум) имеют столбцы OR ORIGINALITY в XML-таблицах. Я нахожу кое-что проще с использованием расширений XML (не ANSI) XML; другие вещи проще с SQL/XML в стиле ANSI. В основном это мытье.Но это явно отсутствующая функция без чистой альтернативы (он сказал, надеясь, что MS слушал). – unbob

Смежные вопросы