Installieren:
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;
Beispiel-XML für jede Zeile:
<Number>314</Number>
Der Job für die Abfrage besteht darin, die Anzahl der Zeilen T
mit dem angegebenen Wert von zu zählen <Number>
.
Es gibt zwei offensichtliche Möglichkeiten, dies zu tun:
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;
Es stellt sich heraus , dass value()
und exists()
erfordert zwei verschiedene Pfaddefinitionen für den selektiven XML - Index an der Arbeit.
create selective xml index SIX_T on dbo.T(XMLDoc) for
(
pathSQL = '/Number' as sql int singleton,
pathXQUERY = '/Number' as xquery 'xs:double' singleton
);
Die sql
Version ist für value()
und die xquery
Version ist für exist()
.
Sie könnten denken, dass ein solcher Index Ihnen einen Plan mit einer netten Suche geben würde, aber selektive XML-Indizes werden als Systemtabelle mit dem Primärschlüssel T
als Hauptschlüssel des gruppierten Schlüssels der Systemtabelle implementiert. Die angegebenen Pfade sind Spalten mit geringer Dichte in dieser Tabelle. Wenn Sie einen Index der tatsächlichen Werte der definierten Pfade benötigen, müssen Sie einen sekundären selektiven Index für jeden Pfadausdruck erstellen.
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);
Der Abfrageplan für exist()
führt eine Suche im sekundären XML-Index durch, gefolgt von einer Schlüsselsuche in der Systemtabelle nach dem selektiven XML-Index (weiß nicht, warum dies erforderlich ist), und führt schließlich eine Suche durch T
, um sicherzustellen, dass tatsächlich solche vorhanden sind Reihen drin. Der letzte Teil ist erforderlich, da zwischen der Systemtabelle und keine Fremdschlüsseleinschränkung besteht T
.
Der Plan für die value()
Abfrage ist nicht so schön. Es wird ein Clustered-Index-Scan T
mit einem Join mit verschachtelten Schleifen gegen eine Suche in der internen Tabelle durchgeführt, um den Wert aus der Spalte mit geringer Dichte abzurufen und schließlich nach dem Wert zu filtern.
Ob ein selektiver Index verwendet werden soll oder nicht, wird vor der Optimierung entschieden, aber ob ein sekundärer selektiver Index verwendet werden soll oder nicht, ist eine kostenbasierte Entscheidung des Optimierers.
Warum wird der sekundäre selektive Index nicht verwendet, wenn die where-Klausel aktiviert ist value()
?
Aktualisieren:
Die Abfragen sind semantisch unterschiedlich. Wenn Sie eine Zeile mit dem Wert hinzufügen
<Number>313</Number>
<Number>314</Number>`
Die exist()
Version würde 2 Zeilen zählen und die values()
Abfrage würde 1 Zeile zählen. Wenn Sie jedoch die hier angegebenen Indexdefinitionen mit der singleton
Direktive SQL Server verwenden, können Sie keine Zeile mit mehreren <Number>
Elementen hinzufügen .
Damit können wir die values()
Funktion jedoch nicht verwenden , ohne [1]
dem Compiler zu garantieren, dass wir nur einen einzigen Wert erhalten. Aus diesem [1]
Grund haben wir eine Top-N-Sortierung im value()
Plan.
Sieht so aus, als würde ich hier eine Antwort finden ...