セットアップ:
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:
<Number>314</Number>
クエリの仕事は、T
指定された値を持つ行の数をカウントすることです<Number>
。
これを行うには、2つの明白な方法があります。
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;
これは、ことが判明value()
し、exists()
仕事への選択的XMLインデックスのための二つの異なるパスの定義が必要です。
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
バージョンがあるためvalue()
とxquery
バージョンがためのものですexist()
。
そのようなインデックスは良いシークを備えたプランを提供すると思うかもしれませんが、選択的なXMLインデックスは、システムテーブルT
のクラスターキーの主キーとしてのプライマリキーを持つシステムテーブルとして実装されます。指定されたパスは、そのテーブルのスパース列です。定義されたパスの実際の値のインデックスが必要な場合は、各パス式に1つずつ、セカンダリ選択インデックスを作成する必要があります。
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);
のクエリプランexist()
は、セカンダリXMLインデックスでシークした後、選択的XMLインデックスのシステムテーブルでキールックアップを実行し(それが必要な理由がわからない)、最後にルックアップを実行T
して実際に存在することを確認しますそこに行。システムテーブルとの間に外部キー制約がないため、最後の部分が必要T
です。
value()
クエリの計画はあまり良くありません。T
内部テーブルのシークに対してネストされたループ結合を使用してクラスター化インデックススキャンを実行し、スパース列から値を取得し、最後に値をフィルター処理します。
選択インデックスを使用するかどうかは、最適化の前に決定されますが、セカンダリ選択インデックスを使用するかどうかは、オプティマイザによるコストベースの決定です。
where句がフィルター処理されるときにセカンダリ選択インデックスが使用されないのはなぜvalue()
ですか?
更新:
クエリは意味的に異なります。値を持つ行を追加する場合
<Number>313</Number>
<Number>314</Number>`
exist()
バージョンは、2行を数えるだろうし、values()
クエリが1行をカウントします。ただし、singleton
SQL Serverディレクティブを使用してインデックス定義をここで指定すると、複数の<Number>
要素を持つ行を追加できなくなります。
ただし、コンパイラーが単一の値のみを取得することを保証values()
する[1]
ように指定せずに関数を使用することはできません。これ[1]
が、value()
プランにトップNソートがある理由です。
ここで答えに近づいているように見えます...