Настроить:
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
с объединением вложенных циклов по запросу во внутренней таблице, чтобы получить значение из разреженного столбца, и, наконец, фильтрует значение.
Если выборочный индекс должен использоваться или нет, то решение принимается перед оптимизацией, но если вторичный селективный индекс должен использоваться или нет, это решение, основанное на затратах оптимизатором.
Почему вторичный селективный индекс не используется при включении предложения where value()
?
Обновить:
Запросы семантически разные. Если вы добавите строку со значением
<Number>313</Number>
<Number>314</Number>`
exist()
версия будет рассчитывать 2 строки и values()
запрос будет рассчитывать 1 строку. Но с определениями индексов, указанными здесь, использование singleton
директивы SQL Server не позволит вам добавить строку с несколькими <Number>
элементами.
Это, однако, не позволяет нам использовать values()
функцию без указания, [1]
чтобы гарантировать компилятору, что мы получим только одно значение. По этой [1]
причине у нас есть Top N Sort в value()
плане.
Похоже, я подхожу к ответу здесь ...