34.6. ルール対トリガ

トリガによって行われる多くの操作はPostgreSQLのルールシステムで実装可能です。 ルールで実装できないものの1つはある種の制約、特に外部キーに関してです。 もし他のテーブルに列の値がなかった場合、条件ルールでコマンドをNOTHINGに書き換えてしまうことも可能ですが、これではデータがだまって消去されてしまい、よいアイディアとはいえません。 有効な値かどうかのチェックが必要で、無効な値についてはエラーメッセージを表示する必要があるなら、このことは今のところトリガを使って行わなければなりません。

一方、ビュー上でINSERTによって起動されたトリガは、データをどこかに退避させ、ビューへの挿入を禁止するルールと同じことができます。 しかしUPDATEまたはDELETEではスキャンされる実データがビューリレーションに存在せず、トリガが呼ばれることがありませんので、ルールと同じことを行なうことができません。 ルールで解決するしかありません。

どちらでも実装できる事項に関してどちらがベストかはデータベースの使用法によります。 トリガはどの行に対しても一度だけ起動します。 ルールは問い合わせツリーを操作するか追加の問い合わせツリーを生成します。 ですから、1つの命令文が多くの行に影響を与える場合、1つの行を処理するたびに呼び出され、その実行を何回も行わなければならないトリガよりも、追加の問い合わせを1つ発行するルールのほうが通常よい働きをします。

ここで、ある状況下でルールとトリガの選択するかを示す例を挙げます。 たとえば、2つのテーブルがあるとします。

CREATE TABLE computer (
    hostname        text,    -- インデックスあり
    manufacturer    text     -- インデックスあり
);

CREATE TABLE software (
    software        text,    -- インデックスあり
    hostname        text     -- インデックスあり
);

2つのテーブルには共に数千の行があって、hostname上のインデックスは一意です。 ルール/トリガは削除されたホストを参照する、softwareの行の削除する制限を実装しなければなりません。 トリガの場合は以下のコマンドを使用します。

DELETE FROM software WHERE hostname = $1;

computerから削除された行一つ一つに対してこのトリガが呼び出されますので、 このコマンドの準備を行ない、計画を保存し、パラメータとしてhostnameを渡すことができます。 ルールの場合は以下のように作成されます。

CREATE RULE computer_del AS ON DELETE TO computer
    DO DELETE FROM software WHERE hostname = OLD.hostname;

ここで別の類の削除を考えてみましょう。

DELETE FROM computer WHERE hostname = 'mypc.local.net';

上のような場合では、computerはインデックスにより(高速に)スキャンされます。 トリガによってこのコマンドが発行された場合もインデックススキャンが使用されます。(高速です。) ルールによる追加コマンドは以下のようになります。

DELETE FROM software WHERE computer.hostname = 'mypc.local.net'
                       AND software.hostname = computer.hostname;

適切なインデックスが設定されていますので、プランナは以下の計画を作成します。

入れ子状ループ
  ->  computerに対しcomp_hostidxを使用したインデックススキャン
  ->  softwareに対しsoft_hostidxを使用したインデックススキャン

ですので、トリガとルールの実装間での速度差はあまりありません。

次の削除処理ではhostnameoldで始まる2,000台すべてのcomputerを削除しようと思います。 方法として2つの有効な問い合わせがあって、1つは 以下のようなものです。

DELETE FROM computer WHERE hostname >= 'old'
                       AND hostname <  'ole'

ルールによって追加されるコマンドは以下のようになります。

DELETE FROM software WHERE computer.hostname >= 'old' AND computer.hostname < 'ole'
                       AND software.hostname = computer.hostname;

これに対する計画は以下のようになります。

ハッシュ結合
  ->  softwareに対する逐次スキャン
  ->  ハッシュ
    ->  computerに対するcomp_hostidxを使用するインデックススキャン

もう1つのコマンドは以下のようなものです。

DELETE FROM computer WHERE hostname ~ '^old';

これにより、ルールによって追加されるコマンド用の実行計画は以下のようになります。

入れ子状ループ
  ->  computerに対するcomp_hostidxを使用するインデックススキャン
  ->  softwareに対するsoft_hostidxを使用するインデックススキャン

これが示していることは、ANDで結合された複数の検索条件が存在する場合、プランナは正規表現版のコマンドでは行っていることですが、computer上のhostnameに対する検索条件をsoftware上のインデックススキャンにも同様に使用できることを理解しないということです。 トリガは削除されるべき2,000台の旧式コンピュータのそれぞれについて1回呼び出され、結果computer上で1回のインデックススキャンとsoftware上で2,000回のインデックススキャンが行われます。 ルールによる実装ではインデックスを使用する2つの問い合わせによって実行されます。 逐次スキャンの場合でもルールがより速いかどうかはsoftwareテーブルの大きさに依存します。 参照するすべてのインデックスブロックがすぐにキャッシュに現れるとしても、トリガによるSPIマネージャ経由の2,000回のコマンドの実行には時間を要します。

最後のコマンドを見てみましょう。

DELETE FROM computer WHERE manufacurer = 'bim';

この文でもcomputerから多くの行が削除される結果となります。 ですので、ここでもトリガはエクゼキュータを通して多くのコマンドを実行することになります。 ルールで作成されるコマンドは以下のようなものです。

DELETE FROM software WHERE computer.manufacurer = 'bim'
                       AND software.hostname = computer.hostname;

このコマンド用の計画もまた前回同様2つのインデックススキャンの入れ子状ループとなります。 computerの別のインデックスを使用する点のみが異なります。

入れ子状ループ
  ->  computerに対するcomp_manufidxを使用するインデックススキャン
  ->  softwareに対するsoft_hostidxを使用するインデックススキャン

いずれの場合においても、ルールシステムが生成する追加コマンドは影響を受ける行数からは多かれ少なかれ独立しています。

まとめると、問い合わせ結果が大きく、プランナがうまく結合条件を設定できないような状況下でのみルールはトリガに比べて明らかに遅くなります。