★PostgreSQLカンファレンス2024 12月6日開催/チケット販売中★
他のバージョンの文書 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

40.7. ルール対トリガ

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

この章ではビューを更新するのにルールを使うことに焦点を当ててきました。 この章の更新ルールの例はすべてビューのINSTEAD OFトリガを使っても実装できます。 特に更新を実行するのに複雑な論理が要求される場合には、そのようなトリガを書くことはしばしばルールを書くよりも簡単です。

どちらでも実装できる事項に関してどちらがベストかはデータベースの使用方法によります。 トリガは各行に対して一度起動します。 ルールは問い合わせを修正するか追加の問い合わせを生成します。 ですから、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から削除された行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;

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

Nestloop
  ->  Index Scan using comp_hostidx on computer
  ->  Index Scan using soft_hostidx on software

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

次の削除処理では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;

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

Hash Join
  ->  Seq Scan on software
  ->  Hash
    ->  Index Scan using comp_hostidx on computer

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

DELETE FROM computer WHERE hostname ~ '^old';

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

Nestloop
  ->  Index Scan using comp_hostidx on computer
  ->  Index Scan using soft_hostidx on software

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

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

DELETE FROM computer WHERE manufacturer = 'bim';

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

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

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

Nestloop
  ->  Index Scan using comp_manufidx on computer
  ->  Index Scan using soft_hostidx on software

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

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