他のバージョンの文書 18 | 17 | 16 | 15 | 14 | 13 | 12 | 11 | 10 | 9.6 | 9.5 | 9.4 | 9.3 | 9.2 | 9.1 | 9.0 | 8.4 | 8.3 | 8.2 | 8.1 | 8.0 | 7.4 | 7.3 | 7.2

11.3. 複数列インデックス #

インデックスは、テーブルの2つ以上の列に定義することができます。 例えば、以下のようなテーブルがあるとします。

CREATE TABLE test2 (
  major int,
  minor int,
  name varchar
);

(例えば、/devディレクトリの内容をデータベースに保持していて)頻繁に下記のような問い合わせを発行するとします。

SELECT name FROM test2 WHERE major = constant AND minor = constant;

このような場合、majorおよびminorという2つの列に1つのインデックスを定義する方が適切かもしれません。

CREATE INDEX test2_mm_idx ON test2 (major, minor);

現在、B-tree、GiST、GINおよびBRINインデックス型でのみ、複数キー列インデックスをサポートしています。 複数キー列を持つことができるかどうかは、INCLUDE列をインデックスに追加できるかどうかとは無関係です。 インデックスはINCLUDE列を含めて最大32列まで持つことができます。 (この上限は、PostgreSQLを構築する際に変更可能です。 pg_config_manual.hファイルを参照してください。)

複数列に対するB-treeインデックスは、インデックス対象列の任意の部分集合を含む問い合わせ条件で使用できますが、もっともインデックスの効率が良いのは、先頭(左側)の列に制約がある場合です。 正確な規則は、先頭の列に対する等価制約、および等価制約を持たない先頭の列に対する不等式制約は、常にスキャン対象のインデックス範囲を制限するために使用されるということです。 これらの列の右側の列に対する制約はインデックスで検査されるため、常にテーブルへのアクセスを適切に抑えますが、必ずしもスキャンしなければならないインデックスの範囲を減らすわけではありません。 B-treeインデックススキャンでスキップスキャン最適化を効果的に適用できる場合は、インデックス検索を繰り返してインデックスを辿るときに、すべての列制約が適用されます。 これにより、(問い合わせの述語に含まれるもっとも重要度の低いインデックス列より前に位置する)1つまたは複数の列に従来の等価制約がない場合でも、読まなければならないインデックスの範囲を減らすことができます。 スキップスキャンは、インデックス列において取り得るすべての値と一致する動的な等価制約を内部的に生成することで機能しています(ただし、問い合わせの述語に含まれる等価制約のない列に対してのみ適用され、かつ生成された制約が問い合わせの述語に含まれる後続の列制約と組み合わせて使用できる場合に限ります)。

たとえば、(x, y)に対するインデックスと問い合わせ条件WHERE y = 7700では、B-treeインデックススキャンでスキップスキャン最適化を適用できる場合があります。 これは通常、問い合わせプランナがそのテーブルで使用可能なインデックスを考慮した時に、WHERE x = N AND y = 7700の検索を、Nで取り得るすべての値(または実際にインデックスに格納されているすべてのxの値)に対して繰り返す方法が最速であると想定している場合に発生します。 この方法は通常、xの個別値が非常に少なく、ほとんどのインデックスをスキップしてスキャンする(ほとんどのリーフページには関連するタプルが含まれないため)とプランナが期待する場合にのみ採用されます。 xの個別値が多い場合、インデックス全体のスキャンが必要になる状況になりうるため、ほとんどの場合プランナはインデックスを使用するよりもシーケンシャルスキャンを好みます。

スキップスキャンの最適化は、問い合わせの述語に有用な制約が少なくともいくつかあるB-treeスキャン中に、選択的に適用することもできます。 たとえば、(a, b, c)にインデックスがあり、WHERE a = 5 AND b >= 42 AND c < 77という問い合わせ条件がある場合、インデックスはa = 5およびb = 42の最初のエントリからa = 5の最後のエントリまでスキャンする必要があるかもしれません。 c >= 77のインデックスエントリはテーブルレベルでフィルタリングする必要はありませんが、インデックス内でスキップすると効果がある場合とない場合があります。 スキップが行われると、スキャンは新しいインデックス検索を開始し、現在のa = 5とb = Nのグループの末尾(つまり、a = 5 AND b = N AND c >= 77の最初のタプルが現れるインデックスの位置)から、次のグルーピングの開始位置(つまり、a = 5 AND b = N + 1の最初のタプルが現れるインデックスの位置)まで位置を変更します。

複数列GiSTインデックスは、インデックス対象列の任意の部分集合を含む問い合わせ条件で使用することができます。 他の列に対する条件は、インデックスで返される項目を制限します。 しかし、先頭列に対する条件が、インデックスのスキャン量を決定するもっとも重要なものです。 先頭列の個別値がわずかな場合、他の列が多くの個別値を持っていたとしても、相対的にGiSTインデックスは非効率的になります。

複数列GINインデックスは、インデックス対象列の任意の部分集合を含む問い合わせ条件で使用することができます。 B-treeやGiSTと異なり、インデックス検索の効果はどのインデックス列が問い合わせ条件で使用されているかに関係なく同じです。

複数列BRINインデックスは、インデックス対象列の任意の部分集合を含む問い合わせ条件で使用することができます。 GINと同様に、またB-treeやGiSTとは異なり、インデックス検索の効果はどのインデックス列が問い合わせ条件で使用されているかに関係なく同じです。 一つのテーブルに対して複数列BRINインデックスを一つ持つ代わりに複数のBRINインデックスを持つ唯一の理由は、異なるpages_per_rangeストレージパラメータを持つためです。

当然ながら、インデックス種類に対して適切な演算子を各列に使用しなければなりません。 他の演算子を含む句は考慮されません。

複数列インデックスは慎重に使用する必要があります。 多くの場合、単一列のインデックスで十分であり、また、その方がディスク領域と時間を節約できます。 テーブルの使用方法が極端に様式化されていない限り、4つ以上の列を使用しているインデックスは、不適切である可能性が高いでしょう。 異なるインデックス構成の利点に関するこの他の説明について11.5および11.9も参照してください。