33.12. 演算子最適化に関する情報

PostgreSQLの演算子定義では、システムに演算子がどう振舞うかに関する有用なことを通知する、幾つかのオプション句を持つことができます。 これらの句により演算子を使用する問い合わせの実行速度がかなり向上しますので、これらの句は適切な時には常に提供しなければなりません。 しかし、提供する時にはそれらが正しいことを確認しなければいけません! 間違って最適化用の句を使用すると、サーバのクラッシュ、不思議な間違った出力、その他有害な事が起こり得ます。 最適化用の句について解らなければ、使用しなくても構いません。 使用された時よりも問い合わせの実行が遅くなるかもしれないというだけです。

PostgreSQLの今後のバージョンで、最適化用の句は更に追加される可能性があります。 ここで説明するものは全て、バージョン7.4.2で有効なものです。

33.12.1. COMMUTATOR

COMMUTATOR句が与えられた場合、それは定義する演算子の交代演算子となる演算子の名前です。 取り得る全ての入力x、yに対して、(x A y)が(y B x)と等しい時、演算子Aは演算子Bの交代演算子であるといいます。 また、BはAの交代演算子となることにも注意して下さい。 例えば、通常、特定のデータ型用の演算子<>は互いの交代演算子になります。 また、通常、演算子+は自身が交代演算子となります。 しかし、通常、演算子-は交代演算子を持ちません。

交代可能演算子の左オペランドの型は、その交代演算子の右オペランドの型と同一で、その逆も又同様です。 したがって、PostgreSQLで交代演算子を検索する時に必要なものは交代演算子の名前のみになりますので、COMMUTATOR句ではそれのみが与えられていればよいです。

インデックスや結合句で使用される演算子では交代演算子の情報を提供することが重要です。 これにより、問い合わせオプティマイザがその句を他の実行計画型で必要とされる形式に"ひっくり返す"ことができるためです。 例えば、tab1.x = tab2.yのようなWHERE句を持った問い合わせを考えてみます。 ここでtab1.xtab2.yはユーザ定義型で、tab2.yにはインデックスが付いていると仮定します。 オプティマイザは、この句をtab2.y = tab1.xという形にひっくり返す方法を知らない限り、インデックススキャンを生成できません。 インデックススキャンは機械的に、演算子の左側にインデックス付けされた列があることを想定しているためです。 PostgreSQLは簡単にこの変形が有効であると前提しません=演算子の作成者がこれが有効であることを、交換演算子情報を持つ演算子であるとマークすることで、指定しなければなりません。

自己交代演算子を定義する場合は、単にそれを指定するだけです。 交代演算子のペアを定義する場合は少し複雑になります。 最初に他の未定義のものを参照するものをどう定義するのかということが問題となります。 この問題には下記の2つの解決方法があります。

33.12.2. NEGATOR

NEGATOR句が与えられた場合、それは定義する演算子の否定子となる演算子の名前です。 入力値、xとyの取り得る全ての値に対して両方の演算子がブール値を返し、(x A y)がNOT (x B y)と等しい場合、演算子Aは演算子Bの否定子であるといいます。 また、BはAの否定子でもあることに注意して下さい。 例えば、ほとんどのデータ型では<>=は否定子のペアとなります。 演算子が自身の否定子になることは決してありません。

交換演算子と異なり、単項演算子のペアは互いに否定子としてマークすることができます。 つまり全てのXに対して(A x)がNOT (B x)と等しいことを意味します。 右単項演算子でも同様です。

演算子の否定子は、COMMUTATOR 句と同様に、その演算子の左オペランド、右オペランドの型と同じものをとらなければなりません。 演算子の名前のみをNEGATOR句で指定しなければなりません。

NOT (x = y)という式をx <> yという形に単純化させることが可能なので、否定子があると問い合わせオプティマイザにとって非常に役に立ちます。 他の再配置の結果としてNOT操作が挿入されることがありますので、この現象は想像よりも頻繁に起こります。

否定子のペアは、上記の交代演算子のペアで説明した方法と同じ方法で定義することができます。

33.12.3. RESTRICT

RESTRICT句が与えられた場合、それは、その演算子用の制限選択評価関数を指定します。 (演算子名ではなく関数名であることに注意して下さい。) RESTRICT句はboolean型を返す二項演算子に対してのみ有効です。 制限選択評価の目的は、現在の演算子と特定の定数値についてのWHERE

column OP constant

の条件を満たすテーブル内の行の割合を推測することです。 この形式を持ったWHERE句によって、どのくらいの行が除外されるのかを通知することで、オプティマイザの手助けをします。 (定数値が左項にあったら何が起こるかという疑問が生じるかも知れませんが、それはCOMMUTATORが存在する理由の1つでもあります。)

新しい制限選択評価関数の記述方法は本章の内容を越えていますが、幸いな事に、大抵の場合システムが持つ標準的な評価関数を使って、多くの独自の演算子用が作成できます。 標準的な制限評価関数には下記のものがあります。

=用のeqsel
<>用のneqsel
<もしくは<=用のscalarltsel
>もしくは>=用のscalargtsel

こうした分類であることは少し奇妙に思えるかもしれませんが、これらには歴とした意味があります。 =は一般的にテーブル内の行の小さな部分を受け付けます。 <>は一般的に小さな部分を除きます。 <は、指定した定数がテーブル列のとる値の範囲(これはよく発生するものです。ANALYZEによって収集される情報で、選択評価関数で使用できるように作成されます。)のどの辺りにあるのかに依存する量の部分を受け付けます。 <=は、同じ定数との比較において<よりも少しだけ大きな部分を受け付けます。 特に大雑把な推測以上のことを行なうのは適切ではありませんので、区別する価値がないといえる位近い値です。 >>=についても同様な事がいえます。

非常に高い/低い選択性をもつ演算子に、それらが本当は全く等しくない場合/等しい場合でおいても、eqselneqselを使用しないでおく事も可能です。 例えば、ほぼ等しい幾何演算子ではテーブルのエントリの小部分にのみに合うものと仮定して eqsel を使用します。

範囲比較のために数スカラに変換された際に、敏感になる型を比較するために、scalarltselscalargtselを使用することも可能です。 可能であるならばsrc/backend/utils/adt/selfuncs.cにある、convert_to_scalar()のルーチンで理解できるところにデータ型を追加して下さい。 (今後、このルーチンはpg_typeシステムカタログの列で識別された、データ型毎の関数で置き換えられなければなりませんが、まだ行われていません。) これを行わなくても動きますが、オプティマイザの推測機能はその機能を発揮できません。

幾何演算子に対しての追加選択関数areaselpositionselcontselsrc/backend/utils/adt/geo_selfuncs.cに書かれています。 この文書を書いている時点では、これらは単なるスタブですが、ともかく使いたい(あるいは改良したい)こともあるでしょう。

33.12.4. JOIN

JOIN句が与えられた場合、それはその演算子用の結合選択評価関数の名前を示します。 (これが演算子名ではなく関数名であることに注意して下さい。) JOIN句はboolean型を返す二項演算子のみ有効です。 結合選択評価の目的は、現在の演算子について、WHERE

table1.column1 OP table2.column2

を満たすテーブル内の行の割合を推測することです。 RESTRICT句の使用と同様、これは、いくつかの取り得る結合シーケンスのうち どれが最も仕事量が少ないように考えられるのかをオプティマイザに計算させることで大きなオプティマイザへの援助となります。

以前と同様、本章でも統合選択評価関数の作成方法は説明しません。 適用できるものがあれば、標準の評価関数の使用をお勧めします。

=用のeqjoinsel
<>用のneqjoinsel
<もしくは<=用のscalarltjoinsel
>もしくは>=用のscalargtjoinsel
2次元面積を基にした比較用のareajoinsel
2次元位置を基にした比較用のpositionjoinsel
2次元含有関係を基にした比較用のcontjoinsel

33.12.5. HASHES

HASHES句が存在する場合、それはシステムに対して、この演算子に基づいた結合にハッシュ結合方法を使っても問題が無い事を伝えます。 HASHES句はboolean型を返す二項演算子にのみ有効です。 実際には、あるデータ型の等価性を求める演算子であった方が良いです。

ハッシュ結合の基礎となっている仮定は、結合演算子は左項と右項の値が同じハッシュコードを持つ時にのみ真を返すことができるということです。 2つの値が異なるハッシュの入れ物に置かれた場合、結合演算子の結果が必ず偽であるという仮定を、結合は暗黙的に行ない、それらを比べる事をしません。 したがって、等価性を表さない演算子にHASHES句を指定することは全く意味がありません。

HASHES印を付けるためには、 結合演算子はハッシュインデックスの演算子クラス内に現れなければなりません。 参照する演算子クラスがまだ存在しませんので、演算子を作成する時にこれは強制されていません。 しかし、演算子クラスが存在しない場合に、このオペレータをハッシュ結合で使用しようとすると、実行時に失敗します。 システムは、演算子の入力データ型用のデータ型特有のハッシュ関数を検索するために、演算子クラスを必要とします。 もちろん、演算子クラスを作成する前に適切なハッシュ関数を提供しなければなりません。

ハッシュ関数を準備する時には注意が必要です。 マシンに依存することから、ハッシュ結合が適切な処理を行なわずに失敗することがあるからです。 例えば、データ型が不要な部分を埋めるビットを持つ可能性がある構造体である場合、(推奨する戦略である、他の演算子と関数を作成して、不要なビットが常にゼロになることを保証しない限り、)その構造体全体を単にhash_anyに渡すことはできません。 この他の例として、IEEE浮動小数点標準を満たすマシンでは、マイナス0とプラス0は異なる値(異なるビット列)になりますが、この比較は等価と定義されます。 浮動小数点数値がマイナス0を持つ可能性があるのであれば、それがプラス0と同じハッシュコードを確実に生成するような処置が必要です。

注意: ハッシュ結合可能演算子の背後の関数はimmutableもしくはstableでなければなりません。 volatileの場合、システムはその演算子を決してハッシュ結合に使用しようとはしません。

注意: ハッシュ結合可能演算子の背後の関数が厳密(strict)な場合、その関数は完全、つまり、2つの非NULL入力に対して、真または偽を返し、決してNULLを返さないものである必要があります。 この規則に従わないと、IN操作におけるハッシュ最適化は間違った結果を生成する可能性があります。 (特に、標準に従うとNULLが正しい答えになるところでINは偽を返すかもしれません。もしくは、NULLという結果に対する準備をしていないといったエラーを生成するかもしれません。)

33.12.6. MERGES (SORT1, SORT2, LTCMP, GTCMP)

MERGES句が存在する場合、それはシステムに対して、この演算子に基づいた結合にマージ結合方法を使っても問題が無い事を伝えます。 MERGES句はboolean型を返す二項演算子にのみ有効です。 実際には、演算子がデータ型またはデータ型のペアの等価性を表すものであることが必要です。

マージ結合は、左側のテーブル、右側のテーブルを順序良くソートし、並列にスキャンするという考えに基づいています。 したがって、両データ型には完全な順序付け機能が必要であり、結合演算子はソート順で"同じ場所"にある値のペアをのみを成功したものとするものである必要があります。 実際問題として、これは、結合演算子は等価性のような振舞いをしなければならないことを意味しています。 左右のデータ型が同じ(または少なくともビット単位での等価)であることが望ましいとされるハッシュ結合とは異なり、マージ結合は論理的な互換性を持つ別の2つのデータ型をとることができます。 例えば、smallintintegerの等価性演算子はマージ結合が可能です。 両方のデータ型を論理的な互換性を保つ順番にソートする演算子のみが必要です。

マージ結合を実行するためには、マージ結合の等価性演算子に関連する4つの演算子をシステムが識別可能であることが必要です。 その演算子とは、左側オペランドのデータ型を対象とした小なり比較演算子、右側オペランドのデータ型を対象とした小なり比較演算子、2つのデータ型間での小なり比較演算子、および2つのデータ型間での大なり比較演算子の4つです。 (マージ結合可能な演算子が2つの異なった入力データ型を持っている場合は、これらは実際には4つの別個の演算子です。ただし、入力型が同一の場合には、3つの小なり演算子は、すべて同じ演算子になります。) これらの演算子は、それぞれSORT1SORT2LTCMP、およびGTCMPオプションとして、個別に名前で指定することができます。 MERGESが指定された際に、これらのうちいずれかが省略されていた場合は、それぞれ<<<、および>というデフォルトの名前がシステムによって与えられます。 また、これら4つの演算子オプションのいずれかが現われた場合には、MERGESが暗黙的に想定されます。 したがって、一部のみを指定して、残りはシステムに指定させることが可能です。

この4つの比較演算子の入力データ型は、マージ結合可能な演算子の入力データ型から推定できます。 したがって、COMMUTATORの場合とまったく同様に、これらの句で必要とされるのは演算子名のみです。 演算子名に特殊なものを使用していない限り、MERGESとだけ入力すれば十分で、詳細はシステムが指定してくれます。 (COMMUTATORNEGATORを使用する場合と同様に、他の演算子を定義する前に等価性演算子を定義すると、システムによりダミーの演算子エントリが作成されます。)

演算子をマージ結合可能演算子とする時、制約が追加されます。 これらの制約は現在CREATE OPERATORではチェックされませんが、いずれも真でない場合に演算子を使用するとエラーが起こる可能性があります。

注意: マージ結合可能演算子の背後にある関数はimmutableもしくはstableでなければなりません。 volatileの場合、システムはその演算子を決してマージ結合に使用しようとはしません。

注意: PostgreSQL7.3より前のバージョンでは、MERGESという略記方法は存在せず、マージ結合可能演算子を作成するためには、SORT1およびSORT2を明示的に指定する必要がありました。 また、LTCMPGTCMPオプションも以前は存在せず、これらの演算子の名前は個別に<>として組み込む必要がありました。