Configuração:
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 de amostra para cada linha:
<Number>314</Number>
O trabalho da consulta é contar o número de linhas Tcom um valor especificado de <Number>.
Existem duas maneiras óbvias de fazer isso:
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;
Acontece que value()e exists()requer duas definições de caminho diferentes para o índice XML seletivo funcionar.
create selective xml index SIX_T on dbo.T(XMLDoc) for
(
pathSQL = '/Number' as sql int singleton,
pathXQUERY = '/Number' as xquery 'xs:double' singleton
);
A sqlversão é para value()e a xqueryversão é para exist().
Você pode pensar que um índice como esse lhe daria um plano com uma boa procura, mas índices XML seletivos são implementados como uma tabela do sistema com a chave primária Tcomo a chave principal da chave em cluster da tabela do sistema. Os caminhos especificados são colunas esparsas nessa tabela. Se você deseja um índice dos valores reais dos caminhos definidos, é necessário criar índices seletivos secundários, um para cada expressão de caminho.
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);
O plano de consulta para exist()faz uma busca no índice XML secundário, seguido de uma pesquisa chave na tabela do sistema para o índice XML seletivo (não sei por que isso é necessário) e, finalmente, faz uma pesquisa Tpara garantir que haja realmente linhas lá. A última parte é necessária porque não há restrição de chave estrangeira entre a tabela do sistema e T.

O plano para a value()consulta não é tão bom. Ele faz uma varredura de índice agrupada Tcom loops aninhados e se junta a uma busca na tabela interna para obter o valor da coluna esparsa e, finalmente, filtra o valor.

Se um índice seletivo deve ser usado ou não, é decidido antes da otimização, mas se um índice seletivo secundário deve ser usado ou não, é uma decisão baseada no custo do otimizador.
Por que o índice seletivo secundário não é usado quando a cláusula where é filtrada value()?
Atualizar:
As consultas são semanticamente diferentes. Se você adicionar uma linha com o valor
<Number>313</Number>
<Number>314</Number>`
a exist()versão contaria 2 linhas e a values()consulta contaria 1 linha. Porém, com as definições de índice conforme especificadas aqui usando a singletondiretiva SQL Server, você não poderá adicionar uma linha com vários <Number>elementos.
No entanto, isso não nos permite usar a values()função sem especificar [1]para garantir ao compilador que obteremos apenas um único valor. Essa [1]é a razão pela qual temos um Top N Sort no value()plano.
Parece que estou me aproximando de uma resposta aqui ...