17.5. ルール対トリガ

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

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

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

例えば、二つのテーブルがあるとします。

    CREATE TABLE computer (
        hostname        text     -- indexed
	manufacturer    text     -- indexed
    );

    CREATE TABLE software (
        software        text,    -- indexed
        hostname        text     -- indexed
    );
二つのテーブルには共に数千の行があって、hostname 上の 索引は一意です。hostname のカラムにはコンピュータの FQDN (絶対ドメイン名)があります。 ルール/トリガは削除されたホストを参照する software による行削除を制限しなければなりません。 トリガは computer から削除されたそれぞれの独立した 行で呼び出されますので、
    DELETE FROM software WHERE hostname = $1;
という命令文を、準備され保存されたプランの中で使用し パラメータ中に 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 を使ったインデックススキャン
トリガとルール実装の間で処理速度の差はそれほどは無いでしょう。 次の削除処理では hostname が 'old' で始まる 2000 全ての computer を削除しようと思います。方法として二つの有効な 問合せがあって、一つは
    DELETE FROM computer WHERE hostname >= 'old'
                           AND hostname <  'ole'
ここで問い合わせのルールに対するプランは
    

    ハッシュ結合
      ->  software 上で順スキャン
      ->  ハッシュ
           -> computer 上で comp_hostidx を使ったインデックススキャン
もう一つの可能な問い合わせは
    DELETE FROM computer WHERE hostname ~ '^old';
で、以下の実行プランを持っています。

   ネストしたループ
      ->  computer 上で comp_hostidx を使ったインデックススキャン
      ->  software 上で soft_hostidx を使ったインデックススキャン   
これが示していることは、プランナは、regexp バージョンでは 行った、AND で結合された複数の検索条件が存在する場合 computer 上 の hostname に対する検索条件で、software 上のインデックススキャン にも同様に使用出来ることを、わかっていないということです。 トリガは削除されるべき 2000 台の旧式コンピュータのいずれかによ って一回呼び出され、結果 computer 上で一回のインデックススキャン と software に対して 2000 のインデックススキャンが行われます。 ルールの実装ではインデックス上で二つの問合せによって実行されます。 逐次スキャンでもルールがより速いかどうかは software テーブルの大きさに依存します。 参照する全てのインデックスブロックが間もなくキャッシュに現れる としても、 SPI マネージャに対する 2000 の問合せの実行には時間を 要します。

最後の問い合わせを見てみましょう。

    DELETE FROM computer WHERE manufacurer = 'bim';
この文でも computer から沢山の行が削除される 結果となります。ここでもトリガはエクゼキュータの実行中に 多くの問い合わせを起動することになります。 ルールプランもまた前回同様二つのインデックス スキャンのネストループとなります。computer の別の インデックスを単に使えば

   ネストループ
      ->  computer に対して comp_manufidx を使用したインデックススキャン
      ->  software に対して soft_hostidx を使用したインデックススキャン 
これらのルール問い合わせの結果は、
    DELETE FROM software WHERE computer.manufacurer = 'bim'
                           AND software.hostname = computer.hostname;
いずれの場合においても、ルールシステムが生成する余分な問合せ は影響を受ける行数からは多かれ少なかれ独立しています。

別の状況として、アクションを実行すべきか否かが変更された属性に依存する、 UPDATEの場合があります。Postgres 6.4では、 ルールイベントにおける属性指定はできなくなっています。 (遅くとも 6.5 あるいはもう少し早くに使えるように なるでしょう。しばしお待ちを。) したがって、現時点で shoelace_log の 例のようなルールを作成する唯一の方法はルール条件を使用することです。初め の問合せの目的リストに出て来ないため、これは対象とする属性が変更されなく ても常に実行される特別な問合せとなります。 再度使えるようになった場合はトリガに対するルールのもう一つの強みになり ます。 この場合、トリガの最適化はその定義により不可能です。特定の属性が変更された 時にのみそのアクションを行なうという処理自体はその(トリガ)関数内部に隠さ れているからです。トリガの定義は行レベルのみに対して認められるので、行が触 られるとトリガが決定が下せるようにトリガが呼び出されなければなり ません。ルールシステムの場合は対象リストを検索することで変更されたかどうか 判りますので、属性が触られていなければその後の問合せをすべて中止することが できます。条件の有無に関わらず、ルールは実行しなければならないことがあれば スキャンを実行します。

問い合わせ結果が大きく、プランナがうまく結合条件を設定できないような 状況下でのみルールはトリガに比べて明かに遅くなります。ルールは大きな 金槌の様なもので、注意を怠ると大怪我をしますがうまく使えばどんな釘でも 正確に頭を叩けます。