組み合わせトリガーには、行前、行後、ステートメント前、ステートメント後の4種類のトリガーイベントのうち、1つから4つを含めるか、INSTEAD OFトリガーイベントのみを含めることができます。
概要
テーブルまたは編集可能なビューに作成された組み合わせトリガーは、複数の時点で発生することができます。組み合わせトリガーは各時点ごとにセグメントに分割され、各時間帯には独自の実行部と例外処理部(オプション)があります。これらすべての部分は共通のPL状態にアクセスできます。この共通状態は、トリガーステートメントの開始時に確立され、トリガーステートメントの終了時に破棄されます。トリガーステートメントがエラーを引き起こした場合でも影響を受けません。
組み合わせトリガーの制限事項
組み合わせDMLトリガーには、以下の制限があります:
OLD、NEW、およびPARENTは宣言部にも、BEFORE STATEMENT部分やAFTER STATEMENT部分にも記述できません。NEWの値を変更できるのは、BEFORE EACH ROW部分のみです。ある時間帯では、別の時間帯で発生した例外を処理できません。
ある時間帯内に
GOTOステートメントが含まれる場合、そのGOTOステートメントのターゲットは同一の時間帯内に存在している必要があります。
トリガーの作成
コンポジットトリガーの宣言部(オプション)では、時間点で区切られたすべてのセグメントで使用できる変数とサブプログラムを宣言できます。トリガーが発生すると、宣言部は任意の時間帯の実行前に一度だけ実行されます。変数とサブプログラムは、トリガー文が適用される時間帯の間存続します。
編集不可なビュー上に作成されるコンポジットトリガーは、実際には単一の時間帯しか持たないため、真の意味でのコンポジットトリガーではありません。編集不可なビュー上に単純なコンポジットトリガーを作成する構文は次のとおりです:
CREATE TRIGGER FOR dml_event_clause ON view_name
[{ FOLLOWS | PRECEDES } other_trigger_name]
COMPOUND TRIGGER
INSTEAD OF EACH ROW IS BEGIN
sql_statement;
END INSTEAD OF EACH ROW;
テーブルまたは編集可能なビュー上に作成されるコンポジットトリガーは、少なくとも1つの時間帯を持ちます。トリガーが複数の時間帯を持つ場合、それらは任意の順序で配置できますが、時間点は重複してはなりません。時間帯が存在しない場合、その時間点では何の操作も発生しません。
コンポジットトリガーには主に以下の時間帯があります:
タイミングポイント |
時間範囲 |
|---|---|
| トリガーステートメントの実行前 | BEFORE STATEMENT |
| トリガーステートメントの実行後 | AFTER STATEMENT |
| トリガーステートメントの影響を受ける各行の前 | BEFORE EACH ROW |
| トリガーステートメントの影響を受ける各行の後 | AFTER EACH ROW |
コンポジットトリガーには初期化部はありませんが、BEFORE STATEMENT では必要な初期化を行うことができます。これは他の時間帯よりも先に実行されるためです。
コンポジットトリガーに BEFORE STATEMENT 部分も AFTER STATEMENT 部分もなく、かつそのトリガー文がいずれの行にも影響を与えない場合、トリガーは決して発生しません。
コンポジットトリガーを使用して、テーブルのサブテーブルに変更情報を記録します。例:
employeesとemp_salariesという2つのテーブルが存在すると仮定します。
obclient [SYS]> CREATE TABLE employees (
employee_id NUMBER PRIMARY KEY,
name VARCHAR2(50),
salary NUMBER(8,2),
department_id NUMBER);
obclient [SYS]> CREATE TABLE emp_salaries (
emp_id NUMBER NOT NULL,
change_date DATE NOT NULL,
salary NUMBER(8,2) NOT NULL,
FOREIGN KEY (emp_id)
REFERENCES employees (employee_id) ON DELETE CASCADE);
同時に、テーブル employees のデータは以下のとおりです。
obclient [SYS]> SELECT * FROM employees;
+-------------+------+--------+---------------+
| EMPLOYEE_ID | NAME | SALARY | DEPARTMENT_ID |
+-------------+------+--------+---------------+
| 1001 | zs | 4400 | 50 |
| 1002 | li | 4840 | 50 |
| 1003 | wong | 5104 | 50 |
| 1004 | xin | 5984 | 50 |
| 1005 | mai | 5984 | 50 |
| 1006 | cai | 5280 | 50 |
| 1007 | cai | 7040 | 50 |
| 1008 | cai | 7084 | 50 |
| 1009 | deng | 6644 | 50 |
| 1010 | yang | 6160 | 50 |
| 1011 | qiu | 7000 | 51 |
+-------------+------+--------+---------------+
11 rows in set
- トリガー
maintain_emp_salariesを作成します。
obclient [SYS]> delimiter /
```sql
obclient [SYS]> CREATE OR REPLACE TRIGGER maintain_emp_salaries
FOR UPDATE OF salary ON employees
COMPOUND TRIGGER
threshold CONSTANT SIMPLE_INTEGER := 7;
TYPE salaries_t IS TABLE OF emp_salaries%ROWTYPE INDEX BY SIMPLE_INTEGER;
sal salaries_t;
idx SIMPLE_INTEGER := 0;
PROCEDURE flush_proc IS
n CONSTANT SIMPLE_INTEGER := sal.count();
BEGIN
FORALL j IN 1..n
INSERT INTO emp_salaries VALUES sal(j);
sal.delete();
idx := 0;
DBMS_OUTPUT.PUT_LINE('Refreshed' || n || ' rows');
END flush_proc;
-- AFTER EACH ROW セグメント:
AFTER EACH ROW IS
BEGIN
idx := idx + 1;
sal(idx).emp_id := :NEW.employee_id;
sal(idx).change_date := SYSTIMESTAMP;
sal(idx).salary := :NEW.salary;
IF idx >= threshold THEN
flush_proc();
END IF;
END AFTER EACH ROW;
-- AFTER STATEMENT セグメント:
AFTER STATEMENT IS
BEGIN
flush_proc();
END AFTER STATEMENT;
END maintain_emp_salaries;
/
```
```sql
obclient [SYS]> SET SERVEROUTPUT ON;
```
部門
50の各従業員の給与を8%引き上げます。obclient [SYS]> UPDATE employees SET salary = salary * 1.08 WHERE department_id = 50 / Query OK, 10 rows affected Rows matched: 10 Changed: 10 Warnings: 0 7 rows refreshed Flushed 7 rows 3 rows refreshed Flushed 3 rows3秒待ってから、以下のコマンドを実行します:
obclient [SYS]> BEGIN DBMS_LOCK.SLEEP(3); END /部門
50の各従業員の給与を10%引き上げます。obclient [SYS]> UPDATE employees SET salary = salary * 1.1 WHERE department_id = 50 / Query OK, 10 rows affected Rows matched: 10 Changed: 10 Warnings: 0 7 rows refreshed Flushed 7 rows 3 rows refreshed Flushed 3 rowsemp_salariesテーブルの従業員テーブルの変更状況を確認します。obclient [SYS]> SELECT emp_id, count(*) num FROM emp_salaries GROUP BY emp_id /クエリ結果は次のとおりです:
+--------+------+ | EMP_ID | NUM | +--------+------+ | 1001 | 2 | | 1002 | 2 | | 1003 | 2 | | 1004 | 2 | | 1005 | 2 | | 1006 | 2 | | 1007 | 2 | | 1008 | 2 | | 1009 | 2 | | 1010 | 2 | +--------+------+ 10 rows in set