Preparar:
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 muestra para cada fila:
<Number>314</Number>
El trabajo para la consulta es contar el número de filas T
con un valor especificado de <Number>
.
Hay dos formas obvias de hacer esto:
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;
Resulta que value()
y exists()
requiere dos definiciones de ruta diferentes para que funcione el índice XML selectivo.
create selective xml index SIX_T on dbo.T(XMLDoc) for
(
pathSQL = '/Number' as sql int singleton,
pathXQUERY = '/Number' as xquery 'xs:double' singleton
);
La sql
versión es para value()
y la xquery
versión es para exist()
.
Puede pensar que un índice como ese le daría un plan con una buena búsqueda, pero los índices XML selectivos se implementan como una tabla del sistema con la clave principal de T
la clave principal de la clave agrupada de la tabla del sistema. Las rutas especificadas son columnas dispersas en esa tabla. Si desea un índice de los valores reales de las rutas definidas, debe crear índices secundarios selectivos, uno para cada expresión de ruta.
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);
El plan de consulta exist()
realiza una búsqueda en el índice XML secundario seguido de una búsqueda clave en la tabla del sistema para el índice XML selectivo (no sé por qué es necesario) y finalmente realiza una búsqueda T
para asegurarse de que realmente hay filas allí. La última parte es necesaria porque no hay restricción de clave externa entre la tabla del sistema y T
.
El plan para la value()
consulta no es tan bueno. Realiza una exploración de índice agrupado T
con una unión de bucles anidados contra una búsqueda en la tabla interna para obtener el valor de la columna dispersa y finalmente filtra el valor.
Si un índice selectivo se debe usar o no se decide antes de la optimización, pero si un índice selectivo secundario se debe usar o no, es una decisión basada en el costo del optimizador.
¿Por qué no se utiliza el índice selectivo secundario cuando se filtra la cláusula where value()
?
Actualizar:
Las consultas son semánticamente diferentes. Si agrega una fila con el valor
<Number>313</Number>
<Number>314</Number>`
la exist()
versión contaría 2 filas y la values()
consulta contaría 1 fila. Pero con las definiciones de índice tal como se especifican aquí, el uso de la singleton
directiva SQL Server evitará que agregue una fila con múltiples <Number>
elementos.
Sin embargo, eso no nos permite usar la values()
función sin especificar [1]
para garantizar al compilador que solo obtendremos un único valor. Esa [1]
es la razón por la que tenemos un Top N Sort en el value()
plan.
Parece que me estoy acercando a una respuesta aquí ...