Impostare:
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 di esempio per ogni riga:
<Number>314</Number>
Il lavoro per la query è contare il numero di righe T
con un valore specificato di <Number>
.
Esistono due modi ovvi per farlo:
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;
Si scopre che value()
e exists()
richiede due diverse definizioni di percorso per il funzionamento dell'indice XML selettivo.
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
versione è per value()
e la xquery
versione è per exist()
.
Potresti pensare che un indice del genere ti darebbe un piano con una buona ricerca ma gli indici XML selettivi sono implementati come una tabella di sistema con la chiave primaria T
come chiave principale della chiave cluster della tabella di sistema. I percorsi specificati sono colonne sparse in quella tabella. Se si desidera un indice dei valori effettivi dei percorsi definiti, è necessario creare indici selettivi secondari, uno per ciascuna espressione di percorso.
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);
Il piano di query per il exist()
fa una ricerca nell'indice XML secondario seguito da una ricerca chiave nella tabella di sistema per l'indice XML selettivo (non so perché sia necessario) e infine fa una ricerca T
per assicurarsi che ci siano effettivamente file lì dentro. L'ultima parte è necessaria perché non esiste alcun vincolo di chiave esterna tra la tabella di sistema e T
.
Il piano per la value()
query non è così bello. Esegue una scansione dell'indice cluster di T
con un loop nidificato unito a una ricerca nella tabella interna per ottenere il valore dalla colonna sparsa e infine filtra il valore.
Se un indice selettivo deve essere utilizzato o meno viene deciso prima dell'ottimizzazione, ma se un indice selettivo secondario deve essere utilizzato o meno è una decisione basata sui costi dell'ottimizzatore.
Perché l'indice selettivo secondario non viene utilizzato quando la clausola where si attiva value()
?
Aggiornare:
Le query sono semanticamente diverse. Se aggiungi una riga con il valore
<Number>313</Number>
<Number>314</Number>`
la exist()
versione conterebbe 2 righe e la values()
query conterrebbe 1 riga. Ma con le definizioni dell'indice come sono specificate qui usando la singleton
direttiva SQL Server ti impedirà di aggiungere una riga con più <Number>
elementi.
Ciò tuttavia non ci consente di utilizzare la values()
funzione senza specificare [1]
per garantire al compilatore che avremo un solo valore. Questo [1]
è il motivo per cui abbiamo un Top N Sort nel value()
piano.
Sembra che sto chiudendo una risposta qui ...