Налаштування:
create table dbo.T
(
ID int identity primary key,
XMLDoc xml not null
);
insert into dbo.T(XMLDoc)
select (
select N.Number
for xml path(''), type
)
from (
select top(10000) row_number() over(order by (select null)) as Number
from sys.columns as c1, sys.columns as c2
) as N;
Зразок XML для кожного рядка:
<Number>314</Number>
Завдання для запиту полягає в підрахунку кількості рядків T
із заданим значенням <Number>
.
Є два очевидних способи зробити це:
select count(*)
from dbo.T as T
where T.XMLDoc.value('/Number[1]', 'int') = 314;
select count(*)
from dbo.T as T
where T.XMLDoc.exist('/Number[. eq 314]') = 1;
Виявляється, що value()
і exists()
вимагає два різних визначення шляху для селективного індексу XML для роботи.
create selective xml index SIX_T on dbo.T(XMLDoc) for
(
pathSQL = '/Number' as sql int singleton,
pathXQUERY = '/Number' as xquery 'xs:double' singleton
);
sql
Версія для value()
і xquery
версії для exist()
.
Ви можете подумати, що подібний індекс дасть вам план із приємним пошуком, але вибіркові індекси XML реалізуються як системна таблиця з первинним ключем T
як головний ключ кластерного ключа системної таблиці. Вказані шляхи є розрідженими стовпцями цієї таблиці. Якщо потрібно індекс фактичних значень визначених шляхів, вам потрібно створити вторинні селективні індекси, по одному для кожного виразу шляху.
create xml index SIX_T_pathSQL on dbo.T(XMLDoc)
using xml index SIX_T for (pathSQL);
create xml index SIX_T_pathXQUERY on dbo.T(XMLDoc)
using xml index SIX_T for (pathXQUERY);
План запитів для exist()
пошуку шукає у вторинному індексі XML з подальшим пошуком ключа в системній таблиці для селективного індексу XML (не знаю, для чого це потрібно) і, нарешті, він робить пошук, T
щоб переконатися, що насправді є ряди там. Остання частина необхідна, оскільки між системною таблицею та системою немає зовнішніх ключових обмежень T
.
План value()
запиту не такий приємний. Він робить кластерне сканування індексів T
з вкладеними петлями, об'єднаними проти пошуку на внутрішній таблиці, щоб отримати значення з розрідженого стовпця і, нарешті, фільтрує значення.
Якщо перед оптимізацією слід використовувати вибірковий індекс чи не вирішувати, але якщо використовується вторинний селективний індекс чи ні, це оптимізатор на основі витрат.
Чому вторинний селективний індекс не використовується, коли пункт фільтр включений value()
?
Оновлення:
Запити семантично різні. Якщо ви додаєте рядок зі значенням
<Number>313</Number>
<Number>314</Number>`
exist()
версія буде розраховувати 2 рядки і values()
запит буде розраховувати 1 рядок. Але з визначеннями індексу, як вони вказані тут, за допомогою singleton
директиви SQL Server не дасть вам додати рядок з декількома <Number>
елементами.
Однак це не дозволяє нам використовувати values()
функцію, не вказуючи [1]
гарантії компілятора, що ми отримаємо лише одне значення. Тому [1]
ми value()
плануємо сортувати верхній N.
Схоже, я закриваюсь у відповіді тут ...