AFAIK, SQL Server по-прежнему не поддерживает функцию position()
в качестве выходных данных, только в качестве критерия фильтрации. Таким образом, нет простого способа нумеровать узлы в их естественном порядке. Все решения, которые я видел, вычисляют положение каждого узла, подсчитывая все предыдущие узлы, создавая, по сути, полукартовое, которое убивает все надежды на производительность при XML приличного размера.
Однако вы можете использовать довольно грязный трюк, который я использовал около 8 лет назад. Вы можете сгенерировать монотонную последовательность чисел и использовать их для поиска узлов в соответствующих позициях. Код ниже иллюстрирует подход:
-- Sample data
declare @t table (
Id int identity(1,1) primary key,
XMLData xml not null
);
insert into @t (XMLData)
values (N'<r>
<Item>First Value</Item>
<Item>Second</Item>
</r>'),
(N'<r>
<Item>Another One</Item>
<Item>123456</Item>
<Item>The last</Item>
</r>');
declare @NodeCount int;
-- Get the largest amount of nodes among the rows of interest
select @NodeCount = max(t.XMLData.value('count(/r/Item)', 'int'))
from @t t;
select t.Id, n.RN, i.c.value('./text()[1]', 'varchar(100)') as [NodeValue]
from @t t
cross join (
-- Generate a number sequence for each /r/Item node in every row
select top (@NodeCount) row_number() over(order by (select null)) as [RN]
from sys.all_objects
) n
-- Get N-th node
cross apply t.XMLData.nodes('/r/Item[position() = sql:column("n.RN")]') i(c)
order by t.Id, n.RN;
Только будьте осторожны при запуске таблицы с несколькими миллионами строк и / или большими BLOB-объектами. В этом примере он работает довольно аккуратно, но я не представляю, насколько он будет эффективен в реальных случаях. Если ваш XML большой или у вас много строк для запроса, лучшим решением может быть предварительная обработка XML путем добавления номеров узлов в качестве атрибутов с использованием языка, отличного от SQL. Например, функция CLR могла бы быть намного лучше для этого.
P.S. Тем не менее, должно быть быстрее, чем цикл ...