Ustawiać:
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;
Przykładowy kod XML dla każdego wiersza:
<Number>314</Number>
Zadanie dla zapytania polega na zliczeniu liczby wierszy T
o określonej wartości <Number>
.
Można to zrobić na dwa oczywiste sposoby:
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;
Okazuje się, że value()
i exists()
do działania selektywnego indeksu XML potrzebne są dwie różne definicje ścieżek.
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
Wersja jest value()
i xquery
wersja jest exist()
.
Możesz pomyśleć, że taki indeks dałby ci plan z ładnym wyszukiwaniem, ale selektywne indeksy XML są implementowane jako tabela systemowa z kluczem podstawowym T
jako kluczem wiodącym klucza klastrowego tabeli systemowej. Podane ścieżki są rzadkimi kolumnami w tej tabeli. Jeśli potrzebujesz indeksu rzeczywistych wartości zdefiniowanych ścieżek, musisz utworzyć wtórne indeksy selektywne, po jednym dla każdego wyrażenia ścieżki.
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);
Plan zapytania dla exist()
wyszukiwania w wtórnym indeksie XML, po którym następuje wyszukiwanie klucza w tabeli systemowej dla selektywnego indeksu XML (nie wiem, dlaczego jest to potrzebne), a na koniec wyszukiwanie w T
celu upewnienia się, że faktycznie istnieją wiersze tam. Ostatnia część jest konieczna, ponieważ między tabelą systemową a nie ma ograniczenia na klucz obcy T
.
Plan value()
zapytania nie jest taki miły. Wykonuje skanowanie indeksu klastrowego T
z zagnieżdżonymi pętlami łączącymi się z szukaniem w wewnętrznej tabeli, aby uzyskać wartość z rzadkiej kolumny i na koniec filtrować wartość.
O tym, czy przed optymalizacją należy zastosować wskaźnik selektywny, decyduje się, ale czy należy zastosować wtórny wskaźnik selektywny, czy nie, jest to decyzja optymalizatora oparta na kosztach.
Dlaczego wtórny indeks selektywny nie jest używany, gdy filtruje klauzula where value()
?
Aktualizacja:
Zapytania są semantycznie różne. Jeśli dodasz wiersz z wartością
<Number>313</Number>
<Number>314</Number>`
exist()
wersja licz 2 wierszy i values()
zapytania licz 1 wiersz. Ale dzięki definicjom indeksów, które są tutaj określone przy użyciu singleton
dyrektywy SQL Server zapobiegnie dodaniu wiersza z wieloma <Number>
elementami.
Nie pozwala to jednak na użycie tej values()
funkcji bez określenia, [1]
czy kompilator otrzyma tylko jedną wartość. To [1]
jest powód, dla którego mamy w planie Top N Sort value()
.
Wygląda na to, że kończę tutaj odpowiedź ...