効率的なインデックス作成は,迅速かつ効率的に結果を探索するデータベースアプリケーションを作成するために重要である.空間データを蓄積するためにデザインされる geometry 型および geography 型に加えて,SQL Server 2008 はまた空間データを使用するための新しいインデックスの型を含んでおり,それは(驚くべきことでもないが)空間インデックスと呼ばれる.本章では空間インデックスがどのように動作し,空間インデックスを使って空間クエリの速度をどのように改善するか説明する.
付記 SQL Server の汎用クラスター化インデックスおよび非クラスター化インデックスは多くの異なるデータ型のインデックスを作成するのに使われ,そこには int 型,char 型および datetime 型を使って蓄積される値を含んでいる.しかし,空間インデックスはgeometry 型および geography 型の列の上でしか作成されず,これらのデータ型の列にのみ空間インデックスは追加できる.
空間インデックスは何をしているのか?
空間操作は複雑で,実行には膨大な計算資源を要する可能性がある.これは,例えば13章で述べたような,2つのジオメトリ間の関係を比較するメソッドを使う時に特に真であり,それは,これらのメソッドが両ジオメトリの点集合に含まれる各点間の関係の評価に依存しているためである.2つの複雑なジオメトリの評価に使われた場合,これらのメソッドは求める結果を取得する前に個別の計算を数千もの実行に巻き込む可能性がある.それゆえ,高価なメソッドである STIntersects(), STDistance() および STContains() を呼ぶ際には可能な限り,時間を短縮する方法を見つけるよう努めるべきである.
例えば,Vineyards というテーブルがあり,世界のブドウ園を表現する Polygon ジオメトリを含んでいると考えてみる.フランスのシャンパーニュ地方に位置するブドウ園を識別したいなら,次のような STWithin() メソッドを使ったクエリを書くだろう(変数 @Champagne はシャンパーニュ地方を表現する Polygon ジオメトリであると仮定する).
SELECT VineyardName FROM Vineyards WHERE VineyardGeometry.STWithin(@Champagne)
このクエリは,すべてのブドウ園を表現する Plygon ジオメトリとシャンパーニュ地方を表現する Polygon ジオメトリとを比較するために計算資源の高価な STWithin() メソッドを使って Vineyards テーブル内のデータ全行を検証しなければならない.今,我々はいくつかのブドウ園,例えばオーストラリアのバロッサバレーにある Jacob’s Creek やカリフォルニアのナパバレーにあるそれらを知っているが,明らかにフランスのシャンパーニュ地方にはない.しかし,SQL Server はこのような常識を適用できないため,STWIthin() を使って結果に含めるべきかどうか確認するため,すべてのジオメトリの交差の評価を行わなくてはならない.これは多大な労力であり,そして実行するには遅く,骨の折れるクエリとなるだろう.
このクエリをもっと効率的に実行するには,正しい地域内,いわば正しい球場に最適に位置する我々の知るそれらの行のみで STWithin() メソッドを呼び出すことで,我々は最初にデータセットを絞り込む必要がある.ここで空間インデックスが入ってくる.
空間クエリを実行する時,SQL Server は結果を取得するために二段階クエリモデルを使い,これは2つのフィルターに関連している.
- 一次フィルター:速く,潜在的な候補レコードを選択し二次フィルターに渡すのに最適なクエリ.一次フィルターにより返る候補セットは実際の結果セットのスーパーセットである.つまり,結果に出現すべきすべての値を含むことが保証されている一方,追加のレコードを含む可能性がある.これら”偽陽性”の結果は二次フィルターで除外されなければならない.
- 二次フィルター:正確な,しかし計算コストの高価な(そしてそれゆえ実行するには遅い)クエリで,問題にしているクエリにより要求された真の結果セットに練り上げるため,一次フィルターで識別された候補結果の上で動作する.
空間インデックスが空間データの列上に存在する時,そのインデックスは空間クエリのための一次フィルターとして使われ,最適な候補結果セットを迅速に識別するためにデザインされている.そうすることで,合致するレコード数を削減し,より遅く正確な二次フィルターにより検証されなければならない.二次フィルターの結果は,クエリにより要求される正確な結果を返すのだが,一次フィルターにより識別された結果セットを,相当する空間メソッドを実行することで得られる.先に与えられた例では,二次フィルターはゆえに STWithin() メソッドである.
空間インデックスが存在しない時,データセットの一次フィルタリングが起こらないため,より遅い二次フィルターがソースデータセットの全行に適用され,それは非常に高価である.
空間インデックスはどう動作しているのか?
空間インデックスの目的を理解した今,クエリの結果に一次フィルターを実行する空間データのインデックス作成についてどう行くか戸惑っているかもしれない.空間インデックスの記述は(どの種類のインデックスでも同様だが)特定の空間クエリのための結果候補セットを迅速に識別してアクセスできるため,何らかの論理順にソートされなければならない.次のデータ型からの値がどのようにインデックス内で順序付けられるか考えてみよう.
- int, money, decimal あるいは float 型を使って蓄積された値は数値順に蓄積される.
- char あるいは varchar 型を使って蓄積された値は(Unicode 相当ではそれぞれ nchar あるいは nvarchae 型)照合順序の順で(大抵はアルファベット順)ソートされる.
- datetime 型を使って蓄積された値は年代順にソートされる.
では,空間内のオブジェクトの位置を定義する geometry 型および geography 型の値のインデックスはどうやって作成するのだろうか?SQL Server 2008 で使われる解決法は(いくつかの他の空間データベースでもそうだが)ジオメトリの存在する空間のエリアにまたがるグリッドシステムを定義することで,インデックスされた各地物は一つ以上のグリッド内のセルと交差する.そのグリッドセルは論理的に配置され順序付けられており,各地物を記述するインデックス記述はそのジオメトリの交差するグリッドセルへの参照を蓄積する.更に詳しくこの概念を説明するため,単純な例を示そう.Figure 14-1 に図示したジオメトリを考えてみよう.
今度はこのジオメトリの上に 4 × 4 の正方形のグリッドセルを被せようとしていることを考えてみよう.そのグリッドのそれらのセルは左から右,上から下の順にナンバリングされており,左上のコーナーにあるセル 1 から始まって増加し,右下コーナーにあるセル 16 で終わる.これを Figure 14-2 に示す.
付記 各グリッドセルへの参照を割り振る SQL Server で使われる実際のナンバリングシステムはここで述べたよりもっと複雑であり,ヒルベルト曲線モデルに基づく.しかし,図示の目的のため単純な増分ナンバリングを用いる.
Figure 14-2 に示したジオメトリは影をつけたセル 2, 3, 4, 5, 6, 7, 9, 10, 11, 14, 15 と交差する.それら 10 個のセルはこのジオメトリの拡張を記述する空間インデックス記述を形作るものを参照する.この単純なモデルに基づいてさえ,このグリッドセルのリストは空間クエリの特定の種類の一次フィルターとして使われるインデックスを作成するのに十分な情報を提供する.例えば,セル 12 のどこかに位置する Point が図示されたジオメトリ内に含まれるかどうかを STWithin() メソッドを使って定義したいとしよう(ジオメトリ B がジオメトリ A 内に含まれるかどうか,B の一部でも A の外周に位置してはならないことを第13章で述べたことを思い出そう).インデックス記述に含まれるセル参照はジオメトリ A の形状に正確にフィットしないが,ジオメトリ全体を覆っている.それゆえ,仮に与えられたセルがジオメトリのインデックス記述に出現しないなら,そのセルに含まれる空間はそのジオメトリの外部に位置しているに違いないと分かる.クエリを実行した時,SQL Server は最初にインデックスに含まれるセルを検査することで一次フィルターを適用する.図示されたジオメトリのためのインデックス記述がセル 12 を含まないため,SQL Server は即座に問題の Point はそのジオメトリ内には含まれないと判断でき,そのクエリは STWithin() メソッドにより要求されるさらなる状態の検査を必要とせず,一次フィルターを使って適切な結果を返すことができる.
付記 ジオメトリのための空間インデックス記述は,ジオメトリに接しているか,一部を覆っているか,全体を覆っているセルを含む.
機能的ではあるが,この方法を使って得られるこのインデックス記述はあまり正確ではない.セル 6 とセル 10 はジオメトリにより完全に覆われているが,いくつかのセル,例えばセル 15 はジオメトリをほとんど含んでいない.セル 11 はジオメトリと接しているだけであるが,接するオブジェクトも互いにインターセクト交差するため,このセルはインデックスに含まれなければならない.これらのセルをインデックスに含めることは一次フィルターにより返るさらなる結果に繋がり,それゆえ二次フィルターの仕事を増やすことになる.例えば,セル 15 の中心に位置する Point がジオメトリ内部に含まれるかどうか定義したいとしよう.空間インデックスを使って,セル 15 は部分的にジオメトリに占められることは分かっているが,インデックス単独に基づいてそのジオメトリがそのセルの中心の Point を含んでいるか否か結論を下すことはできない.この場合,結果を定義するには二次フィルターを適用しなければならない.
インデックスを更に正確に作るには,空間を 8 × 8 グリッドに 64 個のセルに分割配置してグリッド解像度を上げることができる.Figure 14-3 に示す.
合計 64 個のセルを含むグリッドの解像度を上げることで,ジオメトリの周囲によりフィットする形態を得ることができる.インデックスがより正確になるため,これはこのインデックスに基づく一次フィルターが更に選択的になり,二次フィルターに評価されるべき候補のジオメトリがより少なく返ることを意味する.しかし,これは一つの問題を持ち込む.そのジオメトリを記述することができるため,今やインデックスは次のグリッドセルを含まなければならない.11, 12, 13, 17, 18, 19, 20, 21, 25, 26, 27, 28, 29, 33, 34, 35, 36, 37, 42, 44, 45, 51, 52, 53 および 60.そのインデックス記述は今や 26 個のセル値をジオメトリのために含んでいる.元のインデックスの 3 倍近い数である.精度の増加は巨大なインデックスの高価をきたし,実行コストに関連してくる.
この方法を拡張して更に高解像度のグリッドインデックスを宣言することもできる.16 × 16 のグリッドで合計 256 個のセルを含むもので,Figure 14-4 に示す.
Figure 14-4 に占められるセルにより記述される領域は形態により占められる真の領域に最も近く最適化されており,一次フィルターの結果の精度を最適化するものであるが,インデックス記述をさらに複雑化する.インデックスが巨大に成長するほど,それは扱いにくく遅くなり,インデックスを使うことでクエリのパフォーマンスは改善するよりむしろ劣化するポイントに到達する.
一次フィルターが効率的であるためには,インデックスは正確である必要があるが,同時に小さいものである必要もある.では,これらのアプローチの最適な妥協点は何か?SQL Server により使われる解法は,これらの例のような単一のグリッドを使うことではなく,マルチレベルグリッドを定義することである.マルチレベルグリッドは4層のグリッドからなり,お互いの中でネストしている.例えば,最初の第1層はグリッドを 64 個のセルに分割する.次の第2層のグリッドは第1層のセルを更に 64 個のセルに再分割する.第3層は第2層のセルを 64 個のサブセルに再分割し,そうやって第4層に到達する.マルチレベルグリッドを Figure 14-5 に示す.
Figure 14-5 に示したナンバリング変換は,そのグリッドに続く各層にドリルダウンするセル参照を表現しており,Level1.Level2.Level3.Level4 と記述する.例えば,セル 3.9.12.1 は level 1 グリッドのセル3, level 2 グリッドのセル 9, level 3 グリッドのセル 12, level 4 グリッドの最初のセルを参照する.
そのグリッドの各層に含まれるセルのナンバーは独立に,予め定義された3つの解像度のうちの一つに設定される.
- LOW 解像度グリッドは 4 × 4 グリッドに対応し,合計 16 個のセルを含む(Figure 14-2 に示す).
- MEDIUM 解像度グリッドは 8 × 8 グリッドに対応し,合計 64 個のセルを含む(Figure 14-3 に示す).
- HIGH 解像度グリッドは 16 × 16 グリッドに対応し,合計 256 個のセルを含む(Figure 14-4 に示す).
各層のデフォルトの解像度は MEDIUM である.これはデフォルトの空間インデックスは4層のグリッドを含み,それぞれが MEDIUM 解像度であり,第4層では 644 個(約 1670 万)のセルを含んでいることを意味する.すべてのグリッドレベルで解像度を HIGH に増やすと,第4層では最大 2564 個のセル, 43 億個に相当することになる!
付記 マルチレベルグリッド内の各セルは次のグリッドレベルで完全に再分割される.
グリッドからインデックスを構築する
マルチレベルグリッドを使う時,グリッドにより覆われる空間の地物を表現するインデックス記述をどうやって生成するのか戸惑うかもしれない.単純な単一グリッドインデックスでは,本章の最初に論じたように,各ジオメトリのインデックス記述はジオメトリと交差する個別のセルの全リストから構築される.同じことをマルチレベルグリッドでも行う.ジオメトリと交差する全層のグリッドですべてのセルを蓄積する.しかし,そうすることで非効率になり,マルチレベルのいくつかの有益な特性の利点を失いかねない.ジオメトリを記述する空間インデックスは異なるグリッド層由来のセルを含む可能性があるが,全てのグリッドレベルでジオメトリの交差する全てのセルを含む必要はない.空間インデックスに含まれるべきそれらのセルを定義するために,SQL Server は3つのルールを適用する.
- カバーリングルール
- 最深セルルール
- オブジェクトごとのセルルール
これらのルールの目的は,インデックス記述の記述するジオメトリにより正確さを最大化し,それに必要なインデックス記述の総量を最小化するために必要なセルだけを含むインデックス記述を確保することである.各ルールがどうやって操作するか,順番に見ていこう.
カバーリングルール
カバーリングルールとは,どのグリッドレベルでもあるセルが完全にジオメトリにより覆われているなら,そのセルは更に深い層に再分割されるべきではないことを述べている.例えば,level 1 のセルがジオメトリにより完全に覆われているなら,その level 1 のセルに含まれる level 2 のセルはすべて,暗に,完全に覆われていることが分かる(続く level 3, level 4 のセルも同様).ゆえに,この再分割を実行して全層のサブセルを記録することは多くのインデックス空間を占めることとなる一方で新しい情報は何も得られない.この場合,完全に覆われているセルだけをインデックスに蓄積する必要がある.Figure 14-6 にカバーリングルールが例のジオメトリにどのように適用されるか示す.
付記 カバーリングルールはジオメトリに完全に覆われるセルにのみ適用される.一部しか覆われていないなら,さらに低レベルのグリッドに再分割される.
最深セルルール
最深セルルールとは,一部覆われているセルが再分割される時,交差の起こる最深レベルのグリッドに位置するセルのみがインデックスに追加される必要があることを述べている.それらのセルの含まれるより高レベルのセルではない.すべての level 4 のセルは唯一の level 3 のセル内に位置するため,すべての level 3 のセルは唯一の level 2 のセル内に位置するため,ひとたび level 4 のセルが(最深レベル)ジオメトリと交差すると分かれば,暗にその level 4 のセルのより高位のグリッドレベルの各セルはそのジオメトリと一部交差することが分かる.
Figure 14-5 に示すナンバリングシステムに基づく例を使うことは,仮に level 4 のセル 10.2.31.5 がジオメトリと交差するなら,セル 10.2.31 (level3), 10.2 (level 2) および 10 (level 1) もまたそのジオメトリと交差しなくてはならない,なぜならそれらはすでに述べた level 4 のセルを含んでいるからである.最深セルが常に level 4 にあるとは限らないことに注意.Figure 14-6 に示すように,任意のレベルのセルがジオメトリに完全に覆われているなら,それ以上再分割されず,最深セルは完全に覆われているセルのあるグリッドレベルに位置している.
オブジェクトごとのセルルール
最深セルルールおよびカバーリングルールを適用してもなお,複雑なジオメトリを記述する必要のあるインデックス記述は多くの明確なグリッドセルを含んでいる可能性がある.これはジオメトリの拡張を記述するインデックス記述で精度を最大化する一方,インデックスのパフォーマンスを劣化させる可能性がある.オブジェクトごとのセルルールは,各オブジェクトごとに蓄積されるセルの最大数の上限を明示的に設定するものである.セルの再分割がこの上限を超過しそうな状況では,そのセルは再分割されず,その時点でのグリッドレベルのセルは代わりにインデックスに含まれる(最深セルルールにより指示された行動を覆す).オブジェクトごとのセルルールに基づく CELLS_PER_OBJECT パラメータの値は空間インデックスが作成される時に指定しなければならず,範囲は 1 から 8,192 の間に設定される.デフォルト値はオブジェクトごとに 16 セルである.
付記 SQL Server がオブジェクトごとのセルルールを破る唯一の状況は,level 1 のグリッドセルが CELL_PER_OBJECT 値を超える巨大なオブジェクトを覆う必要がある場合である.この場合,SQL Server はオブジェクトがすべて覆われるまで level 1 のグリッドセルを必要な数にまで増やす.
geography 型にグリッドを適用する
本章のこの時点まで,平面の二次元のグリッドシステムから空間インデックスがどう作成されるかを論じてきた.この種のシステムは geometry 型には直接適用できるが,geography 型のインデックス作成についてはどうであろうか?
geometry 型同様,geography 型の空間インデックスは4層のグリッドシステムに基づく.しかし,geography 型の使われる地球の球体モデルに直接グリッドを適用することはできない.まず geography 型の地物を平面上に表現する必要がある.これは,投影に関連する.すでに本書で何度か紹介したが,長方形図法,メルカトル図法,ボンヌ図法,ロビンソン図法が空間結果タブに表示される.geography 型のデータにインデックスを適用するには,SQL Server は他の投影法を使い,次の方法で生成される.
- 地球のモデルの両極に2つの四角錐を置く.2つの四角錐の底面は赤道で接し,それぞれの四角錐は北半球と南半球を完全にカバーする.
- 地球の各半球の地物は四角錐の適切な側面に投影される.
- 四角錐は平坦化され,結合されて単一の投影像を結ぶ.
付記 geography 型でのデータは空間インデックスを適用する時に投影されるわけではない.むしろ,単にそのデータの表現が投影されてインデックスが作成されるだけである.
一旦2つの半球が投影されて単一平面に結合されると,geometry 型と同じ方法で結果のデータにグリッドが適用される.この投影プロセスを Figure 14-7 に示す.
この投影プロセスは geography 型の列に空間インデックスを生成したときはいつでも自動的に透過的に発生する.結果として,geometry 型でも geography 型でもマルチレベルグリッドシステムの基本的な同じルールのセットを適用でき,SQL Server は読者のために残りを処理する.特に明記しない限り,本章で論じた内容の全てはどちらのデータ型のインデックスにも等しく適用されると推定できる.
付記 ”舞台裏”では,Figure 14-7 に示した投影を geography 型の多くの空間操作が利用している.各半球は独立に投影されるため,geography 型のオブジェクトは単一の半球を超えられないという限界の一因となっている.
T-SQL を使って空間インデックスを作成する
背後で空間インデックスがいかにして動作するかの機構を見せた今,空間インデックスを作成する方法の実用的なデモンストレーションを見ていこう.始めるために,その上でインデックスの適用できる簡単なテーブルを作成しよう.データの各行がテーブルに追加されるたびに 1 ずつ増加する identity 列と,無作為の Point の系列の位置を記録する geometry 型および geography 型の列を含むテーブルを作成することにする.テーブルを作成するため,次のクエリを実行する.
CREATE TABLE RandomPoints ( id int identify(1,1), geom geometry, geog geography ) GO
無作為に分散する Point データを 10000 行テーブルに移入するため,次を実行する.
DECLARE @i int = 1 DECLARE @lat float, @long float WHILE @i < 10000 BEGIN SET @lat = (RANF() * 180) - 90 SET @long = (RANF() * 360) - 180 INSERT INTO RandmoPoints (geom, geog) VALUES( geometry::Point(@lat, @long, 4326) geography::Point(@lat, @long, 4326) ) SET @i = @i + 1 END GO
空間データの列を含むテーブルを有している今,空間インデックスを追加できるはずだろう?実際には,まだできない.空間インデックスは,それらのセルをインデックスされるオブジェクトの主キーに関連付けることで,ジオメトリに属するグリッドセルを識別する.インデックス自体は (grid cell id, primary key) の数のペアの形をとる.それゆえ,定義された主キーをもつテーブルにのみ空間インデックスを追加できる.
空間インデックスを生成する前に,RandomPoints テーブルの id 列に主キーを追加する必要があり,次のクエリを使って行うことができる.
ALTER TABLE RandomPoints ADD CONSTRAINT idxCluster PRIMARY KEY CLUSTERED (id ASC) GO
注意 テーブルに空間インデックスを生成するには,テーブルはまずクラスター化主キーを持たなくてはならない.
geometry 型のインデックスを作成する
空間データの列および主キーを含むテーブルを有する今,空間インデックスを追加できる.geometry 型と geography 型とでインデックスの生成される方法は少し違う.まずはじめに,RandomPoints テーブルの geom 列に geometry 型のインデックスがどのように生成されるか,次のクエリに示す構文を使って見ていこう.
CREATE SPATIAL INDEX idxGeometry ON RandomPoints (geom) USING GEOMETRY_GRID WITH ( BOUNDING_BOX = (-180, -90, 180, 90), GRIDS = ( LEVEL_1 = MEDIUM, LEVEL_2 = MEDIUM, LEVEL_3 = MEDIUM, LEVEL_4 = MEDIUM), CELLS_PER_OBJECT = 16 ) GO
この記述の各要素を順番に検証していこう.
- 最初の行は SQL Server において任意の型のインデックスを生成するための標準的な T-SQL 構文を使う.
CREATE INDEX indexnsname ON tablename (columnname)
. この場合,RandomPoints テーブルの geom 列に idxGeometry と呼ばれる SPATIAL インデックスを生成している. USING GEOMETRY_GRID
は GEOMETRY_GRID テッセレーションスキームに基づいてインデックスを生成することを指定する.各データ型は対応するテッセレーションスキームを持つ.今は geometry 型の列にインデックスを生成しようとしているため,GEOMETRY_GRID スキームを指定する.WITH
句はグリッドの構築方法に影響する多くのパラメータを含んでいる.BOUNDING_BOX, GRIDS および CELLS_PER_OBJECT は次で述べる.BOUNDING_BOX
はグリッドがオーバーレイする範囲以上の座標を指定するもので,xmin, ymin, xmax, ymax の順である.geometry 型は無限の平面上でアイテムの位置を記述する.しかし,空間インデックスに必要とされる空間を分割するグリッドは有限の平面内でのみ適用される.それゆえ geometry 型の列に適用されるいかなる空間インデックスのためにも BOUNDING_BOX の範囲を常に指定しなければならない.xmax は xmin より大きく,ymax は ymin よりも大きい限り,境界ボックスの座標値は任意の浮動小数点値である可能性がある.
チップス 境界ボックスはデータのインデックスされる範囲を指定する.境界ボックスの外側に位置するいかなる地物も,テーブルのいかなる空間クエリにも含まれるが,インデックスされることはない.
GRIDS
属性はグリッドの各層に含まれるセルの密度を指定する.LOW 解像度は 4 × 4 グリッドを表現し,MEDIUM 解像度は 8 × 8 グリッドに対応し,HIGH 解像度は 16 × 16 グリッドを指定する.全グリッドレベルのデフォルトの解像度は MEDIUM である.- CELLS_PER_OBJECT 値は,個々の全てのジオメトリのインデックス記述に含まれるセルの最大数を指定する.一度この上限に達すると,セルはそれ以上低層のグリッドレベルに再分割されなくなる.デフォルトの値は 16 である.
たった今記述したパラメータは,空間インデックスに特徴的な属性に関連する.SQL Server において全てのインデックス型に汎用的な,空間インデックスの多くの追加オプションを設定することもできる.たとえば PAD_INDEX や SORT_IN_TEMPDB など.利用可能なオプションの全てのリストは以下を参照のこと.http://msdn.microsoft.com/en-us/library/bb934196.aspx
チップス 既存のすべての空間インデックスに使われるグリッドパラメータは次のテーブルを調べることでチェックできる.sys.spatial_index_tesselation.
geography 型のインデックスを作成する
テーブルの geography 型の列に空間インデックスを生成する構文は geometry 型のそれとよく似ているが,少し違いがある.RandomPoints テーブルの geography 型の列 geog にインデックスを生成するため,次のコードを使うことができる.
CREATE SPATIAL_INDEX idxGeography ON RandomPoints (geog) USING GEOGRAPHY_GRID WITH ( GRIDS = ( LEVEL_1 = MEDIUM, LEVEL_2 = MEDIUM, LEVEL_3 = MEDIUM, LEVEL_4 = MEDIUM), CELLS_PER_OBJECT = 16 )
最初に注意すべき点として,このインデックスは geomgraphy 型に基づいているため,USING GEOGRAPHY_GRID 句を使って,適切なテッセレーショングリッドを使わなければならない.次に,geometry 型と違い,BOUNDING_BOX パラメータは必要ない.全ての geography 型のインデックスグリッドは全球を覆うと仮定されているため,明示的に境界ボックスを指定する必要はない.他方で,geography 型のインデックスを生成するのに使われる設定は geometry 型のインデックスを生成するのに指定したものと全く同じである.
SQL Server Management Studio において空間インデックスを作成する
もし望むなら,SQL Server Management Studio のオプションメニューを使って空間インデックスを追加することもでき,次のステップにより.
- オブジェクトエクスプローラーペインでインデックスを追加したいテーブルの左側にある + アイコンをクリックする.
- テーブルの下に展開されたノードのリストから,インデックスタブを右クリックして「新しいインデックス」を選ぶ.
- 「新しいインデックス」ダイアログボックスが出現する.「インデックスの種類」ドロップダウンリストから,「空間」を選ぶ.
- インデックス名をタイプする.
- 「追加」をクリックする.
- インデックスを追加する列を選ぶよう促される.対応する適切な geography 型あるいは geometry 型の列のチェックボックスをチェックし,OKをクリックする.
- 「新しいインデックス」ダイアログボックスから戻る時,「ページの選択」ヘッダーから「空間」を選ぶ.
- Figure 14-8 に示したように空間インデックス設定ページが出現する.(もし geometry 型のインデックスを作成しようとしているなら)境界ボックスの値,オブジェクトごとのセル数およびグリッドの各層でのグリッド解像度を入力する.
- 適切な値を入力し終えたら,OKをクリックする.
インデックスが作成され,オブジェクトエクスプローラーで見えるようになる.
付記 一度空間インデックスを作成すると主キーをテーブルから削除することはできなくなる.というのは空間インデックスが主キーに依存しているからである.主キーを削除するためには,まず空間インデックスを削除しなければならない.
空間インデックスを使うためのクエリをデザインする
空間インデックスがパフォーマンスを改善できるのは限られた空間クエリだけである.特に,SQL Server は次のメソッドの結果のための一次フィルターとして空間インデックスを使うことをサポートしている.
- Filter()
- STContains()
- STDistance()
- STEquals()
- STIntersects()
- STOverlaps()
- STTouches()
- STWithin()
これらのメソッドのいずれかの結果に基づくデータを抽出するクエリを書いているなら,そして空間インデックスを使うクエリを欲しているなら,ぜひ次の基準に合致しているか確認しなければならない.
- 結果集合をフィルターするためのクエリでこれらのメソッドは WHERE 句に含まれていなくてはならない(SELECT 句や HAVING 句,GROUP BY 句ではない).
- STDIstance() メソッドを除いて,WHERE 句に含まれる式は
A.Method(B) = 1
の形でなくてはならない.
付記 空間インデックスを使うためには,一般的な構文 A.Method(B) = 1
を使ってクエリ式を記述しなければならない.論理的に等価であっても,クエリ SELECT * FROM TABLE WHERE A.STEqual(B) = 1
は空間インデックスを使うことができるが,クエリ SELECT * FROM TABLE WHERE 1 = A.STEqual(B)
はできない.
- STDistance() メソッドを使おうとしている場合,WHERE 句の式は構文
A.STDIstance(B) < x
あるいはA.STDistance(B) <= x
を使って指定しなければならない. - そのメソッドは空間インデックスの定義されたデータの列で実行されなければならない.
了承いただきたいが,たとえこれらの基準に全て合致しても,クエリオプティマイザーはインデックスを使用しない可能性があり,関連するクエリプランのコストに依存する(次のセクションで詳細を論じる).
空間インデックスは非常に選択的なクエリにより使われた時に最も効果を発揮する.つまり,交差するジオメトリ内部のウィンドウがデータセット全体に比較して相対的に小さく選択されるような状況である.横たわるテーブルから選択された行のパーセンテージが増加するにつれ,空間インデックスを使うことが実際クエリパフォーマンスを改善させるどころか劣化させるポイントまで,空間インデックスに対して調べる実行コストはテーブルの全スキャンを実行するコストを上回り始める.
FIgure 14-9 はクエリセットを実行するのに要した時間と 無作為に分散した RandomPoints テーブル内のポイントのセットとをプロットしたグラフに示す.次の一般的な構文に基づく.
DECLARE @Window geometry SET @Window = geometry::STPolyFromText('POLYGON((0 0, 1 0, 1 1, 0 1, 0 0))', 4326) SELECT * FROM RandomPoints WHERE geom.STIntersects(@Window) = 1
クエリを繰り返し実行し,正方形の Polygon @Window の辺の長さを増加するよう指定する.ウィンドウの各大きさごとに,RandomPoints テーブルの geom 列で空間インデックスを使用した場合と使用しない場合でクエリを実行したものである.
Figure 14-9 を見て明らかなように,クエリウィンドウのサイズが非常に小さい時に空間インデックスが最も利益がある.この比較は単純で人工的なデータセットに基づいた結果であることに注意が必要である.特定のアプリケーションにおける空間インデックスを使う相対的な利益は多くの因子に依存し,ここで図示したそれとは変化する可能性がある.
空間インデックスにヒントを提供する
クエリを実行するとなると,SQL Server がデータベースから必要な結果を見つけて返すのに使う一つ以上のアプローチがしばしば存在する.SQL Server は特定のクエリのためにどちらのアプローチ(やプラン)を使うかを決定するためにコストベースのクエリオプティマイザーを採用している.クエリオプティマイザーは各プランの推定コストともに多くの代替クエリプランを発生させる.そのクエリのために選ばれる実際の実行プランは推定コストの最も低いものの一つである.つまり,最も効率の良いクエリである.
一般的には,このプロセスは自動的に起こり,読者は何も心配する必要はない.例えばテーブルにインデックスが存在し,かつそのインデックスを使うクエリオプティマイザーの推定が最も効率的なクエリを生成するなら,そのインデックスは自動的に使われる.空間インデックスは空間クエリをより効率的にし,それで,空間インデックスを生成すると,他には何もすることがない.本当だろうか?不幸なことに,これは実際には正しくない.事実,先述した文には2つの誤りがある.順番にそれらを見ていこう.
第一に,空間インデックスを使うことが常に空間クエリをより効率的にするわけではない.すでに見た通り,GRIDS, CELLS_PER_OBJECT および BOUNDING_BOX パラメータの値を不適切に設定すると,ソーステーブルの空間データに対して直接メソッドを実行するよりも面倒な空間インデックスの使用に至る可能性がある.この場合,クエリオプティマイザーは空間インデックスによるクエリ実行プランを(正しく)使用しない選択をする.というのは高コストに関連するからである.
二番目の誤った仮定は,クエリオプティマイザーはいずれのプランが推定コストが最も低いかに基づいて実行プランを決定するのであって,時々この推定が正しくないことがあるという事実による.これはどの種類のクエリプランでも起こることであるが,推定される空間クエリにコストを正しく割り付けることは特に難しい.結果として,オプティマイザーは空間インデックスを使うクエリプランを選ばない決定をする可能性がある.というのは,他のプランと比較して関連するコストを正しく推定するのに失敗したからである.先述したように,これは空間インデックスが使われないという結果になる.しかしこの場合,クエリオプティマイザーは誤った決定を下しており,そのクエリ実行プランは最適でないものが選ばれる.
チップス クエリオプティマイザーが空間インデックスを使ったプランを選択したかどうかを知るには,クエリを実行する前に SQL Server Management Studio で「クエリ」メニュー→「実際の実行計画を含める」を選択する.仮に実行計画が(空間)クラスター化インデックスシークと呼ばれるステップを含んでいるなら,クエリ実行の一部としてインデックスが使われたことが分かる.このステップにマウスカーソルをホバリングさせると,インデックスの一次フィルターから返った多くの行が含まれる情報ウィンドウが現れる.
空間インデックスがクエリ実行で使われることを確実にしたいなら,index hint を追加して特定のインデックスを使うようクエリオプティマイザーに手動で強制することもできる.インデックスヒントはクエリに内に WITH (INDEX)
句を使うことで,次の例でハイライトしてある.
DECLARE @Region geometry SET @Region = geometry::STPointFromText('POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))', 4326) SELECT * FROM RandomPoints WITH(INDEX(idxGeometry)) WHERE geom.STIntersects(@Region) = 1
このサンプルでは,@Region 変数と交差する geom 列内に含まれるジオメトリが RandomPoints テーブル内に存在する全行をクエリは選択する.WITH INDEX(idxGeometry)
で指定したインデックスヒントは,クエリが idxGeometry インデックスを使って実行されることを確保する.
注意 空間インデックスを使用することで空間クエリのパフォーマンスが改善しても,不適切なインデックスの使用を強制することは同じくらいクエリパフォーマンスを劣化させる.
インデックスを最適化する
空間インデックスが空間クエリの結果に一次フィルターとして動作することで効率的であるなら,それは高速でなくてはならないだけでなく,返る結果の偽陽性の数をも最小限にしなくてはならない.これら2つの目的をインデックスが合致させるのにどううまくやるかは,ほとんど選ばれるグリッド解像度,境界ボックスおよびオブジェクトごとのセル数といったインデックスのパラメータの値によって決まる.
これらのパラメータの個別の使用の適切な値の具体的なガイダンスを与えることは非常に難しい.というのは,それらは問題にしている特定のデータセットの正確な分布に強く依存しているからである.しかし,本章では空間インデックスの設定の定義をする際に心に留めおくべき一般的なアイデアを示そう.
チップス インデックスごとに異なる設定を使って,同じ列に複数の空間インデックスを生成することができる.不均一に分布するデータではこれは特に役立つと分かるかもしれない.
グリッド解像度
正しいグリッド解像度を選択することは,グリッドの各層に含まれるセルの数を選択することでもあるのだが,インデックス(地物周囲の密着度)により提供される精度と,その精度を得るのに必要なグリッドセルの数とのバランスを取ることである.最適なグリッド解像度を達成しようと試みる時,次の因子を考慮すべきである.
- グリッドセルの解像度の設定が低すぎると(例えば相対的に大きい少数のグリッドセルを含むインデックス),一次フィルターはより多くの偽陽性,つまり問題にしているジオメトリと実際には交差しないグリッドセルと交差する地物を返す.これは二次フィルターがしなければならない仕事を増やす結果となり,クエリの劣化につながる.
- グリッドセルの解像度の設定が高すぎると(例えば多数だがそれぞれは個別に小さいグリッドセルを含むインデックス),インデックスは各ジオメトリにより多くの記述を含め,それはインデックスの問い合わせにより長い時間を要し,クエリのパフォーマンスを劣化させることを意味している.
チップス 空間インデックスは空間クエリの一次フィルターとしてのみ使われる.インデックス内のグリッドセルがジオメトリの近似のみを表現していても,クエリで間違った結果を得ることは決してない.というのはそれらは二次フィルターで除去されるからである(Filter() メソッドを除く.これは一次フィルターに基づく結果を返し,二次フィルターは適用されない).
では,特定のデータセットに最適なグリッド解像度はどうやって定義したら良いのか?不幸なことに,従うべき決定的な規則はなく,問題にしている特定のデータセットに大きく依存しており”正解”はない.適切なグリッドサイズを定義するための一つのアプローチは次のようなものである.
- 空間インデックスの全くないテーブルを作成し移入する.テーブルに含まれるデータに対して典型的なクエリを実行し,実行にかかった時間を記録する.これらの結果を使って,インデックスを追加して得られた改善結果に対するベンチマークとする.
- まず全層のグリッドを低解像度を使って geometry 型あるいは geography 型のインデックスを作成する.これは最も一般的なインデックスを生成する.
- ベンチマークで使った元のクエリと同じ設定でクエリを再度実行し,パフォーマンスの違いを評価する(クエリオプティマイザーに新しいインデックスを使うことを確保するためインデックスヒントを使用しなければならないことに注意).
- 既存のインデックスを削除し,各層の解像度を LOW から MEDIUM に増やして新しいインデックスを作成する.
- ベンチマークテストを再実行し,結果を記録する.
- パフォーマンス上の利益を継続して得るまで,各層の解像度を同時にもう一段増やしてステップ4と5を繰り返す.グリッド解像度を増やすことでクエリの実行が遅くなったら,やめて最適なパフォーマンスの設定のインデックスを再作成する(あるいはインデックスを全く使用しない).
このアプローチは空間インデックスのための最適なグリッド解像度の最初の指標を与えるのに使われるが,とても雑な方法である.実務では,最適なグリッド解像度の設定は,境界ボックスとオブジェクトごとのセル数のパラメータに選ばれる値に依存する.テーブルに含まれるデータが変化するなら,最適なインデックスデザインもまた変化する.
境界ボックス
geometry 型の列に適用される空間インデックスの境界ボックスはグリッドのオーバーレイする空間の範囲を定義する.最初の勘は,インデックスの適用されるテーブルに含まれる全データの範囲をカバーする境界ボックスを指定することかもしれないが,それが最善とは限らない.
境界ボックス内に含まれる領域は,固定された数値のセルに分解され,各層のグリッドの解像度で提供されるパラメータで指定される.境界ボックスをより小さく,しかしその同じ境界ボックス内のセル数を同じに保つよう指定することは,各グリッドセルがより小さくなることである.それゆえ,そのグリッドセルはどんな地物でもより正確にフィットする可能性がある.
中央部に密集していて極端に外れている地物がほとんどないデータセットがあると考えてみる.データの全範囲をカバーするようグリッドを指定することは,各グリッドセルが比較的大きい事を意味する.というのは,そのグリッドは遠く離れた地物をカバーするために拡張しなくてはならないためである.データの密集する領域周辺に密着する境界ボックスを指定し,ごく一部の外れた地物をインデックスから除外することで,インデックスはこの領域に含まれるデータの大部分をより正確に描くことができる.インデックスに含まれないこれらの地物は,テーブルに対して問い合わせた時にいかなる結果にも含まれないということを意味しない.インデックスの一次フィルターから取得されないというだけである.
チップス テーブルに含まれる geometry 型のデータの最大座標値および最小座標値に基づいて境界ボックスの範囲を設定できるが,インデックスの境界を狭めることでインデックスのパフォーマンスを改善させる可能性がある.インデックスのグリッドがより粒状になるからである.
オブジェクトごとのセル数
CELLS_PER_OBJECT
パラメータは空間インデックス内で各地物を記述し蓄積するグリッドセルの最大数を明示的に宣言するものである.オブジェクトごとのセル数の最適な数はインデックスの大きさと各記述の精度とがバランスする値である.この最適数は各層で使われるセル解像度と複雑にリンクしており,より高解像度のグリッドはより小さいセルを含んでいるため,そのグリッドの与えられた層でオブジェクトを完全にカバーするのに必要なセルがより多くなることを意味する可能性がある.次はオブジェクトごとのセルの理想的な数を設定しようと試みる時に留意してほしいいくつかの因子である.
- CELLS_PER_OBJECT の設定が低すぎると,最深セルルールとカバーリングルールにもとづき,各インデックス記述はジオメトリを記述するのに必要なセルの総数を含めることができなくなる.そのような場合には,グリッドセルは再分割されず,インデックス記述は正確ではなくなるだろう.
- CELLS_PER_OBJECT の設定が高すぎると,各インデックス記述は多くの数のセルを含むよう成長することになる.これによりインデックスはより正確になるが,より遅いものとなり,それにより,空間クエリの結果を高速化するという空間インデックスを使用する目的を否定することになる.
先述した他のインデックスのパラメータ同様,最適な設定を定義することは,特定のデータセットに基づいて,ある程度の手動での試行錯誤を含む.何の値を設定すべきか明確でないときは,CELLS_PER_OBJECT のデフォルト値 16 を使うとよい.それは多くの状況で適度によく動作する.
要約
本章では,空間インデックスについて学び,それらを使って空間データに対するクエリのパフォーマンスを改善する方法について学んだ.特に,次の点について学んだ.
- 空間インデックスは特定の空間操作の結果のための一次フィルターとして動作する.
- 一次フィルターはクエリの結果を含むことが保証されたジオメトリの候補集合を速く最適に提供するが,追加の偽陽性の結果を含んでいる可能性がある.
- 二次フィルターは一次フィルターの結果を真の結果集合に精製するために使われる.二次フィルターは一次フィルターより遅いが,より正確である.
- 空間地物のインデックスを作成するため,SQL Server は地物をマルチレベルグリッド内のセルに割り付ける.
- SQL Server はカバーリングルール,最深セルルールおよびオブジェクトごとのセル数ルールを適用し,インデックス項目の精度を最大化するよう試みる一方で,それに要するグリッドセル数を最小化するよう試みる.
- geometry 型や geography 型のいずれに対しても空間インデックスを作成でき,T-SQL によっても SQL Server Management Studio によっても可能である.
- 時に,空間インデックスを使用するには,クエリヒントを指定することでクエリを最適化することが必要である.
- 空間インデックスのパフォーマンスに影響する多くの因子があり,それらはスピードと正確性の間のトレードオフを最適化するよう調整されなければならない.