CREATE TRIGGER — 新しいトリガを定義する
CREATE [ OR REPLACE ] [ 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
は新しいトリガを作成します。
CREATE OR REPLACE TRIGGER
は新しいトリガを作成、または、既存のトリガを置き換えます。
作成したトリガは指定したテーブル、ビューまたは外部テーブルと関連付けられ、そのテーブルに特定の操作が行われた時に指定した関数function_name
を実行します。
既存トリガの現在の定義を置き換えるには、既存のトリガ名と親テーブルを指定してCREATE OR REPLACE TRIGGER
を使用してください。
その他の属性はすべて置き換えられます。
トリガでは、起動のタイミングとして、行への操作が開始される前(制約条件のチェックと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
を使用してトリガの動作を変更する時に使用されます。
BEFORE
AFTER
INSTEAD OF
関数の呼び出しをイベントの前に行うか後に行うか、それとも代替として行うかを決定します。
制約トリガではAFTER
としてしか指定することができません。
event
INSERT
、UPDATE
、DELETE
、TRUNCATE
のいずれかが入ります。
このパラメータは、トリガを起動するイベントを指定します。
遷移リレーションが要求される場合を除き、OR
を使用して、複数のイベントを指定することができます。
UPDATE
イベントでは、以下の構文を使用して列リストを指定することができます。
UPDATE OFcolumn_name1
[,column_name2
... ]
このトリガは、UPDATE
コマンドの対象として列挙された列のいずれか少なくとも1つの列が指定された場合に、もしくは列挙された列の1つがUPDATE
の対象の列に依存する生成列である場合に、発行されます。
INSTEAD OF UPDATE
イベントでは列リストを使用できません。
遷移リレーションを要求する場合も列リストを指定することはできません。
table_name
トリガを作成するテーブル、ビューまたは外部テーブルの名前です(スキーマ修飾名も可)。
referenced_table_name
制約で参照される他のテーブルの名前(スキーマ修飾可)です。 このオプションは外部キー制約で使用されるものであり、一般利用を推奨しません。 これは制約トリガでのみ指定することができます。
DEFERRABLE
NOT DEFERRABLE
INITIALLY IMMEDIATE
INITIALLY DEFERRED
トリガのデフォルトのタイミングです。 これらの制約オプションについてはCREATE TABLE文書を参照してください。 これは制約トリガでのみ指定することができます。
REFERENCING
このキーワードは、トリガの文の遷移リレーションへのアクセスを提供する1つまたは2つのリレーション名の宣言の直前に起きます。
OLD TABLE
NEW TABLE
この句は、それに続くリレーション名が更新前イメージの遷移リレーションなのか、更新後イメージの遷移リレーションなのかを示します。
transition_relation_name
この遷移リレーションについて、トリガ内で使用される(修飾されていない)名前です。
FOR EACH ROW
FOR EACH STATEMENT
このパラメータは、トリガ関数を、トリガイベントによって影響を受ける行ごとに1回起動するか、SQL文ごとに1回のみ起動するかを指定します。
どちらも指定されない場合は、FOR EACH STATEMENT
がデフォルトです。
制約トリガはFOR EACH ROW
のみ指定することができます。
condition
トリガ関数を実際に実行するか否かを決定する論理式です。
WHEN
が指定された場合、condition
がtrue
を返す場合のみ関数が呼び出されます。
FOR EACH ROW
トリガでは、WHEN
条件で、それぞれOLD.
、column_name
NEW.
と記述することで、古い行の値、新しい行の値、またはその両方の列を参照することができます。
当然ながらcolumn_name
INSERT
トリガではOLD
を参照することはできませんし、DELETE
トリガではNEW
を参照することはできません。
INSTEAD OF
トリガはWHEN
条件をサポートしません。
現時点ではWHEN
条件に副問い合わせを含めることはできません。
制約トリガでは、WHEN
条件の評価は遅延されず、行の更新操作が行われた直後に発生することに注意してください。
この条件が真と評価されなかった場合、トリガは遅延実行用のキューに入りません。
function_name
ユーザが提供する関数です。この関数は、引数を取らずtrigger
型を返すよう定義されます。トリガが起動した時に実行されます。
CREATE TRIGGER
の構文では、キーワードFUNCTION
とPROCEDURE
は等価ですが、参照されている関数はどちらの場合でも関数でなければならず、プロシージャであってはなりません。
ここでキーワードPROCEDURE
を使うことは、歴史的なものであり廃止予定です。
arguments
トリガ実行時に関数に渡される引数をカンマで区切ったリストで、省略可能です。 引数として指定するのは、リテラル文字列定数です。 単純な名前および数値定数を記述できますが、全て文字列に変換されます。 関数内でこれらの引数にアクセスする方法について調べるためには、トリガ関数を実装した言語の説明を参照してください。 通常の関数引数とは異なる場合があります。
テーブルに対してトリガを作成もしくは変更するには、ユーザがそのテーブルに対しTRIGGER
権限を持っている必要があります。
またユーザはトリガ関数に対しEXECUTE
権限を持たなければなりません。
トリガを削除するためにはDROP TRIGGER
を使用してください。
パーティションテーブルに行レベルのトリガを作ると、存在するパーティションすべてに同一の「クローン」トリガがつくられます。そして、後から作られたり追加されるパーティションも同一のトリガを含みます。
子パーティションに名前が衝突するトリガがすでにある場合には、CREATE OR REPLACE
TRIGGER
によってそのトリガをクローントリガで置き換えない限りエラーになります。
パーティションが親から切り離された場合、クローントリガは削除されます。
列指定のトリガ(UPDATE OF
構文で定義されたトリガ)は、列挙された列のいずれかがcolumn_name
UPDATE
コマンドのSET
リスト内に対象として指定された場合に発行されます。
BEFORE UPDATE
トリガにより行の内容になされた変更は考慮されないため、トリガが発行されない場合であっても、列の値が変更されることはあります。
反対に、UPDATE ... SET x = x ...
のようなコマンドは、列の値が変更されませんが、x
列に対するトリガが発行されます。
BEFORE
トリガにおいてWHEN
条件は関数が実行される、またはされそうな直前に評価されます。
このためWHEN
の使用はトリガ関数の先頭で同一の条件を試験することと実質的に違いはありません。
この条件で確認できるNEW
行が現在の値であり、それまでのトリガで変更されている可能性があることに、特に注意して下さい。
またBEFORE
トリガのWHEN
条件では、NEW
行のシステム列(ctid
など)はまだ設定されていないので、検査することができません。
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
ルールによって取り扱われる場合は、ビューを指定した元の文の代わりに、そのルールが出力した文が実行されます。
それにより、発行されるトリガは、置き換えられた文によって指定されたテーブルに付けられたトリガとなります。
同様に、ビューが自動更新可能ならば、操作は、ビューの基底テーブル上の操作に自動的に書き換えられる文によって取り扱われます。
その結果、発行されるのは基底テーブルの文レベルのトリガとなります。
パーティションテーブルや継承した子テーブルがあるテーブルを変更したとき、明示的に指定されたテーブルに付けられている文レベルのトリガが発行されますが、パーティションや子テーブルに付けられている文レベルのトリガは発行されません。
対照的に、問合せ中で明示的に指定されていなくても、行レベルのトリガはすべての変更されたパーティションや子テーブルに対して発行されます。
REFERENCING
句で指定された遷移リレーションのある文レベルのトリガが定義されている場合、行の変更前イメージおよび変更後イメージは、変更されたすべてのパーティションおよび子テーブルから見ることができます。
継承された子テーブルの場合、行イメージはトリガが付けられたテーブルに存在する列だけしか含みません。
現在のところ、遷移リレーションのある行レベルトリガは、パーティションや継承した子テーブルには定義できません。
また、パーティションテーブルのトリガはINSTEAD OF
とすることはできません。
現在のところ、OR REPLACE
オプションは制約トリガに対してはサポートされていません。
トリガのテーブルに対してすでに更新動作を実行したトランザクション内で、既存のトリガを置き換えることはお勧めしません。 既に決定されたトリガ発行、もしくは発行の判断のうち既に決定された部分は再考されることはありませんので、結果は驚くべきものになるかもしれません。
自身でトリガのコードを書かなくても、よくある問題を解決するために使うことのできる組み込みのトリガ関数が多少あります。9.28を参照してください。
テーブルaccounts
の行が更新される直前に関数check_account_update
を実行します。
CREATE TRIGGER check_update BEFORE UPDATE ON accounts FOR EACH ROW EXECUTE FUNCTION check_account_update();
UPDATE
コマンドでbalance
列が対象として指定されている場合にのみ関数を実行するよう、そのトリガ定義を修正します。
CREATE OR REPLACE 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の拡張です。
OR REPLACE
オプションも同じです。