CREATE TRIGGER — 新しいトリガを定義する
CREATE [ CONSTRAINT ] TRIGGERname{ BEFORE | AFTER | INSTEAD OF } {event[ OR ... ] } ONtable_name[ FROMreferenced_table_name] [ NOT DEFERRABLE | [ DEFERRABLE ] [ INITIALLY IMMEDIATE | INITIALLY DEFERRED ] ] [ REFERENCING { { OLD | NEW } TABLE [ AS ]transition_relation_name} [ ... ] ] [ FOR [ EACH ] { ROW | STATEMENT } ] [ WHEN (condition) ] EXECUTE { FUNCTION | PROCEDURE }function_name(arguments) ここでeventは以下のいずれかを取ることができます。 INSERT UPDATE [ OFcolumn_name[, ... ] ] DELETE TRUNCATE
CREATE TRIGGERは新しいトリガを作成します。
作成したトリガは指定したテーブル、ビューまたは外部テーブルと関連付けられ、そのテーブルに特定の操作が行われた時に指定した関数function_nameを実行します。
トリガでは、起動のタイミングとして、行への操作が開始される前(制約条件のチェックとINSERT、UPDATEまたはDELETEが行われる前)、操作が完了した後(制約条件がチェックされ、INSERT、UPDATEまたはDELETEが完了した後)、操作の代わり(ビューにおける挿入、更新、削除の場合)のいずれかを指定することができます。
イベントの前または代わりにトリガが起動する場合、そのトリガは対象行に対する操作を省略したり、(INSERTとUPDATEの操作時のみ)挿入する行を変更したりすることができます。
イベントの後にトリガが起動する場合、他のトリガの影響を含む全ての変更が、トリガに対して「可視」状態となります。
FOR EACH ROW付きのトリガは、その操作によって変更される行ごとに1回ずつ呼び出されます。
例えば、10行に影響を与えるDELETE操作は、対象リレーション上のすべてのON DELETEトリガを、削除される各行について1回ずつ、個別に10回呼び出すことになります。
反対に、FOR EACH STATEMENT付きのトリガは、その操作によって何行変更されたかにかかわらず、任意の操作ごとに1回のみ実行されます
(変更対象が0行となる操作でも、適用できるすべてのFOR EACH STATEMENTトリガが実行されます)。
トリガイベントのINSTEAD OFとして発行されるように指定されたトリガはFOR EACH ROW印を付けなければなりません。
またビュー上にのみ定義することができます。
ビューに対するBEFOREおよびAFTERトリガはFOR EACH STATEMENT印を付けなければなりません。
さらに、FOR EACH STATEMENTのみですが、トリガをTRUNCATEに対して発行するように定義することができます。
以下の表にどの種類のトリガがテーブル、ビュー、外部テーブルに対して使用できるかをまとめます。
| いつ | イベント | 行レベル | 文レベル |
|---|---|---|---|
BEFORE | INSERT/UPDATE/DELETE | テーブル、および外部テーブル | テーブル、ビューおよび外部テーブル |
TRUNCATE | — | テーブル | |
AFTER | INSERT/UPDATE/DELETE | テーブルおよび外部テーブル | テーブル、ビューおよび外部テーブル |
TRUNCATE | — | テーブル | |
INSTEAD OF | INSERT/UPDATE/DELETE | ビュー | — |
TRUNCATE | — | — |
またトリガ定義では、論理値のWHEN条件を指定することができ、これによってトリガを発行すべきかどうかが判定されます。
行レベルのトリガでは、WHEN条件は行の列の古い値、新しい値、またはその両方で検証することができます。
文レベルのトリガでもWHEN条件を持たせることができますが、条件としてテーブル内のどの値も参照することができませんので、この機能はあまり有用ではありません
同一イベントに同じ種類の複数のトリガが定義された場合、名前のアルファベット順で実行されます。
CONSTRAINTオプションが指定された場合、このコマンドは制約トリガを作成します。
これは、SET CONSTRAINTSを使用してトリガを発行するタイミングを調整することができるという点を除き、通常のトリガと同じです。
制約トリガは(外部テーブルではない)普通のテーブルのAFTER ROWトリガでなければなりません。
トリガイベントを引き起こした文の最後、またはそれを含むトランザクションの最後のいずれかで発行することができます。
後者の場合、遅延と呼ばれます。
SET CONSTRAINTSを使用することで、強制的に待機中の遅延トリガの発行を即座に行わせることができます。
制約トリガは、実装する制約に違反した時に例外を発生するものと想定されています。
REFERENCINGオプションは遷移リレーションの収集を有効にします。
遷移リレーションとは現在のSQL文によって挿入、削除または修正されたすべての行を含む行集合です。
この機能により、トリガはSQL文によって行われたことを、一度に1行ずつだけではなく、全体のビューとして参照することができます。
このオプションは、制約トリガではないAFTERトリガについてのみ使うことができます。
また、トリガがUPDATEトリガの場合、column_nameのリストを指定してはいけません。
OLD TABLEは一度だけ指定することができ、UPDATEまたはDELETEのときに実行されるトリガにのみ指定できます。
これは文によって更新または削除されるすべての行の更新前イメージを含む遷移リレーションを作成します。
同様に、NEW TABLEは一度だけ指定することができ、UPDATEまたはINSERTのときに実行されるトリガにのみ指定できます。
これは、文によって更新または挿入されるすべての行の更新後イメージを含む遷移リレーションを作成します。
SELECTはまったく行を変更しないため、SELECTトリガを作成することはできません。
SELECTトリガが必要に見える問題には、ルールやビューが現実的な解決策を提供できるでしょう。
トリガに関するより詳細については、第39章を参照してください。
name
新しいトリガに付与する名前です。
同じテーブルの他のトリガと異なる名前にする必要があります。
名前をスキーマ修飾することはできません。
トリガはそのテーブルのスキーマを引き継ぎます。
制約トリガの場合、この名前がSET CONSTRAINTSを使用してトリガの動作を変更する時に使用されます。
BEFOREAFTERINSTEAD OF
関数の呼び出しをイベントの前に行うか後に行うか、それとも代替として行うかを決定します。
制約トリガではAFTERとしてしか指定することができません。
event
INSERT、UPDATE、DELETE、TRUNCATEのいずれかが入ります。
このパラメータは、トリガを起動するイベントを指定します。
遷移リレーションが要求される場合を除き、ORを使用して、複数のイベントを指定することができます。
UPDATEイベントでは、以下の構文を使用して列リストを指定することができます。
UPDATE OFcolumn_name1[,column_name2... ]
このトリガはUPDATEコマンドの対象として列挙された列のいずれか少なくとも1つの列が指定された場合に発行されます。
INSTEAD OF UPDATEイベントでは列リストを使用できません。
遷移リレーションを要求する場合も列リストを指定することはできません。
table_nameトリガを作成するテーブル、ビューまたは外部テーブルの名前です(スキーマ修飾名も可)。
referenced_table_name制約で参照される他のテーブルの名前(スキーマ修飾可)です。 このオプションは外部キー制約で使用されるものであり、一般利用を推奨しません。 これは制約トリガでのみ指定することができます。
DEFERRABLENOT DEFERRABLEINITIALLY IMMEDIATEINITIALLY DEFERREDトリガのデフォルトのタイミングです。 これらの制約オプションについてはCREATE TABLE文書を参照してください。 これは制約トリガでのみ指定することができます。
REFERENCINGこのキーワードは、トリガの文の遷移リレーションへのアクセスを提供する1つまたは2つのリレーション名の宣言の直前に起きます。
OLD TABLENEW TABLEこの句は、それに続くリレーション名が更新前イメージの遷移リレーションなのか、更新後イメージの遷移リレーションなのかを示します。
transition_relation_nameこの遷移リレーションについて、トリガ内で使用される(修飾されていない)名前です。
FOR EACH ROWFOR EACH STATEMENT
このパラメータは、トリガ関数を、トリガイベントによって影響を受ける行ごとに1回起動するか、SQL文ごとに1回のみ起動するかを指定します。
どちらも指定されない場合は、FOR EACH STATEMENTがデフォルトです。
制約トリガはFOR EACH ROWのみ指定することができます。
condition
トリガ関数を実際に実行するか否かを決定する論理式です。
WHENが指定された場合、conditionがtrueを返す場合のみ関数が呼び出されます。
FOR EACH ROWトリガでは、WHEN条件で、それぞれOLD.、column_nameNEW.と記述することで、古い行の値、新しい行の値、またはその両方の列を参照することができます。
当然ながらcolumn_nameINSERTトリガではOLDを参照することはできませんし、DELETEトリガではNEWを参照することはできません。
INSTEAD OFトリガはWHEN条件をサポートしません。
現時点ではWHEN条件に副問い合わせを含めることはできません。
制約トリガでは、WHEN条件の評価は遅延されず、行の更新操作が行われた直後に発生することに注意してください。
この条件が真と評価されなかった場合、トリガは遅延実行用のキューに入りません。
function_name
ユーザが提供する関数です。この関数は、引数を取らずtrigger型を返すよう定義されます。トリガが起動した時に実行されます。
CREATE TRIGGERの構文では、キーワードFUNCTIONとPROCEDUREは等価ですが、参照されている関数はどちらの場合でも関数でなければならず、プロシージャであってはなりません。
ここでキーワードPROCEDUREを使うことは、歴史的なものであり廃止予定です。
argumentsトリガ実行時に関数に渡される引数をカンマで区切ったリストで、省略可能です。 引数として指定するのは、リテラル文字列定数です。 単純な名前および数値定数を記述できますが、全て文字列に変換されます。 関数内でこれらの引数にアクセスする方法について調べるためには、トリガ関数を実装した言語の説明を参照してください。 通常の関数引数とは異なる場合があります。
テーブルにトリガを作成するには、ユーザがそのテーブルに対しTRIGGER権限を持っている必要があります。
またユーザはトリガ関数に対しEXECUTE権限を持たなければなりません。
トリガを削除するためにはDROP TRIGGERを使用してください。
列指定のトリガ(UPDATE OF 構文で定義されたトリガ)は、列挙された列のいずれかがcolumn_nameUPDATEコマンドのSETリスト内に対象として指定された場合に発行されます。
BEFORE UPDATEトリガにより行の内容になされた変更は考慮されないため、トリガが発行されない場合であっても、列の値が変更されることはあります。
反対に、UPDATE ... SET x = x ...のようなコマンドは、列の値が変更されませんが、x列に対するトリガが発行されます。
BEFOREトリガにおいてWHEN条件は関数が実行される、またはされそうな直前に評価されます。
このためWHENの使用はトリガ関数の先頭で同一の条件を試験することと実質的に違いはありません。
この条件で確認できるNEW行が現在の値であり、それまでのトリガで変更されている可能性があることに、特に注意して下さい。
またBEFOREトリガのWHEN条件では、NEW行のシステム列(oidなど)はまだ設定されていないので、検査することができません。
AFTERトリガにおいて、WHEN条件は行の更新を行った直後に評価され、文の最後でトリガを発行するためにイベントを保持すべきかどうかを決定します。
このためAFTERトリガのWHEN条件は真を返さない場合、イベントを保持する必要もありませんし、文の最後の行を再度取り出す必要もありません。
これにより、トリガをわずかな行のみに対して発行する必要がある場合、多くの行を変更する文を非常に高速にすることができます。
場合によっては1つのSQLコマンドが2種類以上のトリガを発行することがあります。
例えば、ON CONFLICT DO UPDATE句のあるINSERTでは、挿入と更新の両方の操作が発生するかもしれないので、必要に応じて両方の種類のトリガを発行します。
トリガに提供される遷移リレーションはトリガのイベント種類毎に個別のものです。
従って、INSERTトリガには挿入された行だけが見え、一方でUPDATEトリガには更新された行だけが見えます。
ON UPDATE CASCADEやON DELETE SET NULLなど外部キーを強制する動作によって起こる行の更新や削除は、それを起こしたSQLコマンドの一部であるとみなされます(このような動作は決して遅延実行されないことに注意してください)。
影響を受けたテーブルの関連するトリガが発行されるため、これはSQLコマンドの種類と直接には一致しないトリガが発行される別のケースとなります。
単純な場合、遷移リレーションを要求するトリガは、元となる1つのSQLコマンドによって起こされたテーブルへのすべての変更を、一つの遷移リレーションとして見ることになります。
しかし、遷移リレーションを要求するAFTER ROWトリガの存在により、一つのSQLコマンドによって発生する外部キーを強制する動作が複数のステップに分割され、各ステップがそれぞれの遷移リレーションを持つという場合もあります。
そのような場合、すべての文レベルのトリガは1つの遷移リレーションの集合の作成に対して1度ずつ呼び出され、それによりトリガが遷移リレーション内の変更された行をちょうど一度だけ見ることを確実にしています。
ビューに付けられている文レベルのトリガは、ビューに対する操作が行レベルのINSTEAD OFトリガによって取り扱われた時にのみ発行されます。
ビューに対する操作がINSTEAD OFルールによって取り扱われる場合は、ビューを指定した元の文の代わりに、そのルールが出力した文が実行されます。
それにより、発行されるトリガは、置き換えられた文によって指定されたテーブルに付けられたトリガとなります。
同様に、ビューが自動更新可能ならば、操作は、ビューの基底テーブル上の操作に自動的に書き換えられる文によって取り扱われます。
その結果、発行されるのは基底テーブルの文レベルのトリガとなります。
パーティションテーブルに行レベルのトリガを作ると、存在するパーティションすべてに同一のトリガがつくられます。そして、後から作られたり追加されるパーティションも同一のトリガを含みます。
パーティションテーブルのトリガはAFTERだけです。
パーティションテーブルや継承した子テーブルがあるテーブルを変更したとき、明示的に指定されたテーブルに付けられている文レベルのトリガが発行されますが、パーティションや子テーブルに付けられている文レベルのトリガは発行されません。
対照的に、問合せ中で明示的に指定されていなくても、行レベルのトリガはすべての変更されたパーティションや子テーブルに対して発行されます。
REFERENCING句で指定された遷移リレーションのある文レベルのトリガが定義されている場合、行の変更前イメージおよび変更後イメージは、変更されたすべてのパーティションおよび子テーブルから見ることができます。
継承された子テーブルの場合、行イメージはトリガが付けられたテーブルに存在する列だけしか含みません。
現在のところ、遷移リレーションのある行レベルトリガは、パーティションや継承した子テーブルには定義できません。
PostgreSQL 7.3より前のバージョンでは、トリガ関数の戻り値の型を、trigger型ではなくプレースホルダであるopaque型として宣言する必要がありました。
古いダンプファイルのロードをサポートするため、CREATE TRIGGERではopaque型を返すよう宣言された関数を受け入れます。
しかし、注意を促すメッセージを表示し、宣言された関数の戻り値型をtriggerに変換します。
テーブルaccountsの行が更新される直前に関数check_account_updateを実行します。
CREATE TRIGGER check_update
BEFORE UPDATE ON accounts
FOR EACH ROW
EXECUTE FUNCTION check_account_update();
上と同じです。
しかし、列balanceがUPDATEコマンドの対象として指定された場合のみ実行されます。
CREATE TRIGGER check_update
BEFORE UPDATE OF balance ON accounts
FOR EACH ROW
EXECUTE FUNCTION check_account_update();
以下の構文では、列balanceが実際に変更された場合のみ関数が実行されます。
CREATE TRIGGER check_update
BEFORE UPDATE ON accounts
FOR EACH ROW
WHEN (OLD.balance IS DISTINCT FROM NEW.balance)
EXECUTE FUNCTION check_account_update();
何か変更された場合のみにaccountsの更新のログを取る関数を呼び出します。
CREATE TRIGGER log_update
AFTER UPDATE ON accounts
FOR EACH ROW
WHEN (OLD.* IS DISTINCT FROM NEW.*)
EXECUTE FUNCTION log_account_update();
ビューの背後にあるテーブルに行を挿入するために、各行に対して関数view_insert_rowを実行します。
CREATE TRIGGER view_insert
INSTEAD OF INSERT ON my_view
FOR EACH ROW
EXECUTE FUNCTION view_insert_row();
各文に対して関数check_transfer_balances_to_zeroを実行して、transferの行が相殺してゼロになることを確認します。
CREATE TRIGGER transfer_insert
AFTER INSERT ON transfer
REFERENCING NEW TABLE AS inserted
FOR EACH STATEMENT
EXECUTE FUNCTION check_transfer_balances_to_zero();
各行に対して関数check_matching_pairsを実行して、対応する組み合わせに対して同じ時に(同じ文により)変更されていることを確認します。
CREATE TRIGGER paired_items_update
AFTER UPDATE ON paired_items
REFERENCING NEW TABLE AS newtab OLD TABLE AS oldtab
FOR EACH ROW
EXECUTE FUNCTION check_matching_pairs();
39.4には、C言語で作成されたトリガ関数の完全な例があります。
PostgreSQLにおけるCREATE TRIGGER文は標準SQLのサブセットを実装したものです
現在は、PostgreSQLには、次の機能がありません。
AFTERトリガの遷移テーブル名はREFERENCING句を使って標準SQLの方法で指定できますが、FOR EACH ROWトリガで使用される行変数はREFERENCING句で指定することができません。
それはトリガ関数が書かれる言語に依存する方法で利用できますが、各言語によって決まった方法になります。
一部の言語は、REFERENCING句がOLD ROW AS OLD NEW ROW AS NEWとなっているかのように動作します。
標準SQLでは列を指定したUPDATEトリガでも遷移テーブルを使うことができますが、その場合遷移テーブルで見ることができる行の集合はトリガの列リストに依存します。
これは現在のところPostgreSQLでは実装されていません。
PostgreSQLでは、トリガ動作として、ユーザ定義関数の実行しか認めていません。
標準では、多数の他のSQLコマンドを実行させることができます。
例えば、トリガ動作としてCREATE TABLEを実行させることも可能です。
この制限を回避する方法は簡単です。必要なコマンドを実行するユーザ定義関数を作成すればよいのです。
SQLでは、複数のトリガは、作成時刻順に起動すべきであると規定しています。 PostgreSQLでは名前順です。この方が便利だと考えられるからです。
SQLでは、数珠繋ぎの削除に対するBEFORE DELETEは、数珠繋ぎのDELETEが完了した後に発行するものと規定しています。
PostgreSQLでは、BEFORE DELETEは常に削除操作よりも前に、それも起点となる削除よりも前に行われます。
この方がより一貫性があると考えられいます。
また、参照整合性に関する動作により引き起こされる更新を実行している間に、BEFOREトリガが行を更新し、更新を妨げるような場合の動作も標準に従わないものがあります。
これは、制約違反となるかもしれませんし、参照整合性制約に合わないデータを格納してしまうかもしれません。
ORを使用して単一トリガに複数の動作を指定する機能は、標準SQLに対するPostgreSQLの拡張です。
TRUNCATEでのトリガ発行機能、および、ビューに対する文レベルのトリガの定義機能は標準SQLに対するPostgreSQLの拡張です。
CREATE CONSTRAINT TRIGGERは標準SQLに対するPostgreSQLの拡張です。