トリガーは、特定のタイプの操作が実行されるたびに、データベースが特定の機能を自動的に実行するという仕様です。 トリガーは、テーブル (パーティション化されているかどうか) 、ビュー、および外部テーブルにアタッチできます。
テーブルおよび外部テーブルでは、
INSERT、UPDATE、またはDELETE操作の前または後に、変更された行ごとに1回、またはperSQLstatementを1回実行するようにトリガーを定義できます。UPDATEトリガーは、UPDATEステートメントのset句に特定の列が記載されている場合にのみ、起動するように設定できます。 トリガーはTRUNCATEステートメントに対しても発生します。 トリガーイベントが発生すると、イベントを処理するために適切なタイミングでトリガーの関数が呼び出されます。ビューでは、
INSERT、UPDATE、DELETE操作の代わりにトリガーを定義して実行できます。 このようなINSTEAD OFトリガーは、ビューで変更する必要がある行ごとに1回実行されます。 トリガーの関数は、ビューの基になるベーステーブルに必要な変更を実行し、必要に応じて、変更された行をビューに表示されるように返します。 ビューのトリガーは、INSERT、UPDATE、またはDELETE操作の前または後に、SQL文ごとに1回実行するように定義することもできます。 ただし、このようなトリガーは、ビューにINSTEAD OFトリガーもある場合にのみ発生します。 それ以外の場合は、ビューをターゲットとするステートメントを、その基になるベーステーブルに影響を与えるステートメントに書き直す必要があります。
トリガーを作成する前に、トリガー関数を定義する必要があります。 トリガー関数は、引数を取らず、型triggerを返す関数として宣言する必要があります。 (トリガー関数は、通常の関数引数の形式ではなく、特別に渡されたTriggerData構造を介して入力を受け取ります。)
適切なトリガー機能が作成されると、CREATE triggerでトリガーが確立されます。 同じトリガー関数を複数のトリガーに使用できます。
PostgreSQLでは、[行ごと] トリガーと [文ごと] トリガーの両方が提供されます。
行ごとのトリガーでは、トリガー関数は、トリガーを起動したステートメントの影響を受ける行ごとに1回呼び出されます。
対照的に、ステートメントごとのトリガーは、そのステートメントの影響を受ける行数に関係なく、適切なステートメントが実行されたときに1回だけ呼び出されます。
特に、ゼロ行に影響を与えるステートメントでも、該当するステートメントごとのトリガーが実行されます。 これらの2つのタイプのトリガーは、それぞれ行レベルトリガーと文レベルトリガーと呼ばれることがあります。 TRUNCATEのトリガーは、行ごとではなく、ステートメントレベルでのみ定義できます。
トリガーは、操作の前、後、または代わりに起動するかどうかによっても分類されます。 これらは、それぞれ、BEFOREトリガ、AFTERトリガ、およびINSTEAD OFトリガと呼ばれる。
ステートメントレベルの
BEFOREは、ステートメントが何かを開始する前に自然に起動し、ステートメントレベルのAFTERは、ステートメントの最後に起動します。 これらのタイプのトリガは、テーブル、ビュー、または外部テーブル上で定義することができる。行レベルの
BEFOREは、特定の行が操作される直前に起動し、行レベルのAFTERは、ステートメントの最後 (ただし、ステートメントレベルのAFTERが起動する前) に起動します。 これらのタイプのトリガーは、ビューではなく、テーブルと外部テーブルでのみ定義できます。INSTEAD OFトリガは、ビューでのみ、行レベルでのみ定義することができます。ビュー内の各行が操作が必要であると識別されるとすぐに起動します。
継承またはパーティション階層内の親テーブルを対象とするステートメントでは、影響を受ける子テーブルのステートメントレベルのトリガーは実行されません。親テーブルのステートメントレベルのトリガーのみが実行されます。 ただし、影響を受ける子テーブルの行レベルのトリガーが発生します。 INSERTにON CONFLICT DO UPDATE句が含まれている場合、行レベルBEFORE INSERTトリガーと行レベルBEFORE UPDATEトリガーの両方の効果を、更新された行の最終状態から明らかな方法で適用できます。 ただし、実行する行レベルのBEFOREトリガの両方のセットに対して、EXCLUDED列参照は必要ありません。 挿入 /更新される行を変更する行レベルのトリガーが [BEFORE] [INSERT] と [BEFORE] [UPDATE] の両方である場合、驚くべき結果が発生する可能性を考慮する必要があります (これは、変更がほぼ同等であっても、それらがべき等ではない場合でも問題があります) 。 ステートメントレベルのUPDATEトリガーは、ON CONFLICT DO UPDATEが指定されたときに、UPDATEの影響を受けた行があるかどうかに関係なく (および代替のUPDATEパスが取得されたかどうかに関係なく) 実行されます。 ON CONFLICT DO UPDATE句を持つINSERTは、最初にステートメントレベルのBEFORE INSERTトリガー、次にステートメントレベルのBEFORE UPDATEトリガーを実行し、次にステートメントレベルのAFTER UPDATEトリガー、最後にステートメントレベルのAFTER INSERTトリガーを実行します。
パーティションテーブルのUPDATEによって行が別のパーティションに移動する場合、元のパーティションからDELETEとして実行され、その後に新しいパーティションにINSERTが実行されます。 この場合、すべての行レベルBEFORE UPDATEトリガーとすべての行レベルBEFORE DELETEトリガーが元のパーティションで発生します。 次に、すべての行レベルのBEFORE INSERTトリガーが宛先パーティションで起動されます。 これらすべてのトリガーが移動中の行に影響を与える場合、驚くべき結果の可能性を考慮する必要があります。 AFTER ROWトリガーに関する限り、AFTER DELETEおよびAFTER INSERTトリガーが適用されますが、UPDATEがDELETEおよびINSERTに変換されているため、AFTER UPDATEトリガーは適用されません。 ステートメントレベルのトリガーに関する限り、行の移動が発生した場合でも、DELETEまたはINSERTトリガーはいずれも実行されません。UPDATEステートメントで使用されているターゲットテーブルで定義されているUPDATEトリガーのみが実行されます。
ステートメントごとのトリガーによって呼び出されるトリガー関数は、常にNULLを返す必要があります。 行ごとのトリガーによって呼び出されるトリガー関数は、選択した場合、テーブル行 (HeapTuple型の値) を呼び出し元のエグゼキュータに返すことができます。 操作の前に起動された行レベルのトリガーには、次の選択肢があります。
現在の行の操作をスキップするには、
NULLを返すことができます。 これは、トリガーを呼び出した行レベルの操作 (特定のテーブル行の挿入、変更、または削除) を実行しないようにエグゼキュータに指示します。行レベルの
INSERTおよびUPDATEトリガーのみの場合、返される行は挿入される行になるか、更新される行を置き換えます。 これにより、トリガー関数は、挿入または更新される行を変更できます。
これらの動作のいずれも発生させない行レベルのBEFOREトリガーは、渡されたのと同じ行 (つまり、INSERTトリガーとUPDATEトリガーの場合はNEW行、DELETEトリガーの場合はOLD行) を結果として返すように注意する必要があります。
行レベルのINSTEAD OFトリガーは、ビューの基になるベーステーブルのデータを変更しなかったことを示すNULLを返すか、渡されたビュー行 (INSERTおよびUPDATE操作の場合はNEW行、DELETE操作の場合はOLD行) を返します。 null以外の戻り値は、トリガーがビューで必要なデータ変更を実行したことを示すために使用されます。 これにより、コマンドの影響を受ける行数のカウントがインクリメントされます。 INSERTおよびUPDATE操作の場合のみ、NEW行を変更してから返すことができます。 これにより、INSERT RETURNINGまたはUPDATE RETURNINGによって返されるデータが変更され、提供されたデータとまったく同じデータが表示されない場合に便利です。
操作後に実行された行レベルのトリガーでは戻り値は無視されるため、NULLを返すことができます。 生成された列にはいくつかの考慮事項が適用されます。 格納された生成列は、[BEFORE] トリガーの後および [after] トリガーの前に計算されます。 したがって、生成された値はAFTERトリガーで検査できます。 BEFOREトリガーでは、予想されるように、OLD行には古い生成値が含まれていますが、NEW行にはまだ新しい生成値が含まれていないため、アクセスする必要はありません。 C言語インターフェイスでは、この時点で列の内容は定義されていません。上位レベルのプログラミング言語では、BEFOREトリガーのNEW行に格納されている生成された列にアクセスできないようにする必要があります。 BEFOREトリガーで生成された列の値に対する変更は無視され、上書きされます。 同じリレーション上の同じイベントに対して複数のトリガーが定義されている場合、トリガーはトリガー名のアルファベット順に起動されます。 BEFOREおよびINSTEAD ofトリガーの場合、各トリガーによって返される可能性のある変更行が次のトリガーへの入力になります。 BEFOREまたはINSTEAD OFトリガーのいずれかがNULLを返す場合、その行の操作は中止され、後続のトリガーは (その行に対して) 起動されません。
トリガー定義は、トリガーを起動するかどうかを判断するためにテストされるブールWHEN条件を指定することもできます。 行レベルのトリガーでは、WHEN条件は、行の列の古い値および /または新しい値を調べることができます。 (ステートメントレベルのトリガーにはWHEN条件を設定することもできますが、この機能はそれほど有用ではありません。)
BEFOREトリガーでは、関数が実行される、または実行される直前にWHEN条件が評価されるため、WHENを使用することは、トリガー関数の開始時に同じ条件をテストすることと大きく異なりません。ただし、
AFTERトリガーでは、行の更新が発生した直後にWHEN条件が評価され、ステートメントの最後にトリガーを起動するイベントがキューにあるかどうかが判断されます。 したがって、AFTERトリガーのwhen条件がtrueを返さない場合、イベントをキューに入れたり、ステートメントの最後に行を再フェッチしたりする必要はありません。 これにより、いくつかの行に対してトリガーを実行する必要がある場合、多くの行を変更するステートメントの大幅な高速化が発生する可能性があります。INSTEAD OFトリガーはWHEN条件をサポートしません。
通常、行レベルのBEFOREトリガーは、挿入または更新されるデータのチェックまたは変更に使用されます。 たとえば、BEFOREトリガーを使用して、現在の時刻をtimestamp列に挿入したり、行の2つの要素が一致していることを確認したりできます。 行レベルのAFTERトリガーは、他のテーブルに更新を反映したり、他のテーブルとの整合性チェックを行うために最も賢明に使用されます。 この分業の理由は、AFTERトリガーは行の最終値を確認していることを確認できますが、BEFOREトリガーは確認できないためです。その後に他のBEFOREトリガーが発生する可能性があります。 BEFOREまたはAFTERをトリガーする特定の理由がない場合は、操作に関する情報をステートメントの最後まで保存する必要がないため、BEFOREの方が効率的です。
トリガー関数がSQLコマンドを実行すると、これらのコマンドが再びトリガーを起動する可能性があります。 これはカスケードトリガーと呼ばれます。 カスケードレベルの数に直接的な制限はない。 カスケードでは、同じトリガーを再帰的に呼び出すことができます。たとえば、INSERTトリガーで同じテーブルに追加の行を挿入するコマンドが実行され、INSERTトリガーが再び起動される場合があります。 このようなシナリオで無限再帰を回避するのは、トリガープログラマの責任です。
トリガーが定義されているときは、そのトリガーに対して引数を指定できます。 トリガー定義に引数を含める目的は、同様の要件を持つ異なるトリガーが同じ関数を呼び出すことを可能にすることです。 一例として、引数として2つの列名を取り、現在のユーザを一方に、現在のタイムスタンプを他方に入れる一般化されたトリガ関数が存在し得る。 適切に記述されると、このトリガ関数は、トリガされる特定のテーブルとは無関係である。 したがって、同じ関数を適切な列を持つ任意のテーブルのINSERTイベントに使用して、たとえばトランザクションテーブル内のレコードの作成を自動的に追跡できます。 updateトリガーとして定義されている場合、最後の更新イベントを追跡するためにも使用できます。
トリガーをサポートする各プログラミング言語には、トリガー入力データをトリガー関数で使用できるようにする独自のメソッドがあります。 この入力データには、トリガーイベントのタイプ (INSERTやUPDATEなど) と、CREATE triggerにリストされた引数が含まれます。 行レベルのトリガーの場合、入力データには、INSERTおよびUPDATEトリガーのNEW行、および /またはUPDATEおよびDELETEトリガーのOLD行も含まれます。
既定では、ステートメントレベルのトリガーには、ステートメントによって変更された個々の行を調べる方法はありません。 ただし、AFTER STATEMENTトリガーでは、影響を受ける行のセットをトリガーで使用できるようにするために、遷移テーブルの作成を要求できます。AFTER ROWトリガーは、遷移テーブルを要求することもできます。これにより、テーブル内の変更の合計と、現在起動されている個々の行の変更を確認できます。 遷移テーブルを検査する方法は、再び、使用されているプログラミング言語に依存するが、典型的なアプローチは、遷移テーブルを、トリガ関数内で発行されたSQLコマンドによってアクセスすることができる読み取り専用の一時テーブルのように動作させることである。