組み合わせDMLトリガーは、テーブルまたは編集可能なビューに作成でき、複数の時間点でのトリガーをサポートします。組み合わせトリガーは各時間点ごとにセグメントに分割され、各時間帯には独立した実行部分と例外処理部分(オプション)が含まれます。
適用対象
この内容はOceanBaseデータベースEnterprise Editionにのみ適用されます。OceanBaseデータベースCommunity EditionではMySQLモードのみ提供されています。
組み合わせDMLトリガーの構文
最もシンプルな組み合わせDMLトリガーの構文は以下のとおりです:
CREATE trigger_name FOR dml_event_clause ON view_name
COMPOUND TRIGGER
INSTEAD OF EACH ROW IS BEGIN
sql_statement;
END INSTEAD OF EACH ROW;
組み合わせDMLトリガーの宣言部(オプション)は、変数とサブルーチンを定義するために使用されます。トリガーが発火すると、まず宣言部が実行され、そこで定義された変数とサブルーチンは、トリガー句が適用される期間中存在します。
組み合わせDMLトリガーには少なくとも1つの時間帯が含まれます。複数の時間帯は任意の順序で配置できますが、時間点は重複してはなりません。
| タイミング | 期間 |
|---|---|
| トリガー文の実行前 | BEFORE STATEMENT |
| トリガー文の実行後 | AFTER STATEMENT |
| トリガー文の影響を受ける各行の前 | BEFORE EACH ROW |
| トリガー文の影響を受ける各行の後 | AFTER EACH ROW |
組み合わせDMLトリガーには初期化部は含まれません。BEFORE STATEMENT は他の時間帯よりも先に実行されるため、初期化操作に使用できます。組み合わせDMLトリガーに BEFORE STATEMENT 部分も AFTER STATEMENT 部分もなく、かつそのトリガー句がいずれの行にも影響を与えない場合、トリガーは決して発火しません。
組み合わせDMLトリガーの制限
組み合わせDMLトリガーには以下の制限があります:
OLD、NEW、PARENTは宣言部、BEFORE STATEMENT部分、またはAFTER STATEMENT部分には記述できません。NEWの値を変更できるのはBEFORE EACH ROW部分のみです。- ある時間帯で発生した例外を別の時間帯で処理することはできません。
- ある時間帯内に
GOTOステートメントが含まれている場合、GOTOステートメントのターゲット位置は同一時間帯内でなければなりません。
組み合わせDMLトリガーの利点
組み合わせDMLトリガーは、バッチSQLを使用する場合や、トリガー句が多くの行に影響を与えるシナリオにおいて、パフォーマンス上の利点があります。
以下の例では、このステートメントが4つの時間帯を含む組み合わせDMLトリガーを発火させると仮定します。tbl1 テーブルの col1 列が col2 より大きい各行に対して、トリガーの BEFORE EACH ROW および AFTER EACH ROW 部分が実行されます。ただし、BEFORE STATEMENT 部分は INSERT ステートメントの実行前にのみ実行され、AFTER STATEMENT 部分は INSERT ステートメントの実行後にのみ実行されます。
INSERT INTO tbl2
SELECT col1
FROM tbl1
WHERE tbl1.col1 > col2;
さらに、組み合わせDMLトリガーをバッチ挿入ステートメントと組み合わせて使用することもできます。組み合わせDMLトリガーは行を累積した後、他のテーブルに送信することで、定期的なバッチデータ挿入を実現できます。複合DMLトリガーを使用することで、変異テーブルエラー(エラーコードOBE-04091)を回避することも可能です。
例
複合トリガーを使用して、変更された情報をテーブルのサブテーブルに記録します。
obclient> CREATE TABLE emp_sal (
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);
Query OK, 0 rows affected
obclient> delimiter /
obclient> CREATE OR REPLACE TRIGGER maintain_emp_sal
FOR UPDATE OF salary ON employees
COMPOUND TRIGGER
threshhold CONSTANT SIMPLE_INTEGER := 3;
TYPE sal_typ IS TABLE OF emp_sal%ROWTYPE INDEX BY SIMPLE_INTEGER;
sal sal_typ;
idx SIMPLE_INTEGER := 0;
PROCEDURE flush_proc IS
n CONSTANT SIMPLE_INTEGER := sal.count();
BEGIN
FORALL j IN 1..n
INSERT INTO emp_sal 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 >= threshhold THEN
flush_proc();
END IF;
END AFTER EACH ROW;
-- AFTER STATEMENTセグメント:
AFTER STATEMENT IS
BEGIN
flush_proc();
END AFTER STATEMENT;
END maintain_emp_sal;
/
Query OK, 0 rows affected
obclient> delimiter ;
obclient> SET SERVEROUTPUT ON;
Query OK, 0 rows affected
/* 部門50の各従業員の給与を8%引き上げる */
obclient> UPDATE employees
SET salary = salary * 0.8
WHERE department_id = 50;
Query OK, 10 rows affected
Rows matched: 10 Changed: 10 Warnings: 0
Refreshed 3 rows
Refreshed 3 rows
Refreshed 7 rows
Flushed 7 rows
Refreshed 3 rows
Refreshed 1 row
Refreshed 3 rows
Flushed 3 rows
/* 3秒待機 */
obclient> BEGIN
DBMS_LOCK.SLEEP(3);
END;
/
Query OK, 1 row affected(3.01 sec)
/* 部門50の各従業員の給与を1.1%引き上げる */
obclient> UPDATE employees
SET salary = salary * 1.1
WHERE department_id = 50;
Query OK, 10 rows affected
Rows matched: 10 Changed: 10 Warnings: 0
Refreshed 3 rows
Refreshed 3 rows
Refreshed 7 rows
Flushed 7 rows
Refreshed 3 rows
Refreshed 1 row
Refreshed 3 rows
Flushed 3 rows
/* emp_salテーブル内の従業員テーブルの変更状況を確認する */
obclient> SELECT emp_id, count(*) num
FROM emp_sal
GROUP BY emp_id;
+--------+------+
| EMP_ID | NUM |
+--------+------+
| 120 | 2 |
| 121 | 2 |
| 122 | 2 |
| 123 | 2 |
| 124 | 2 |
| 125 | 2 |
| 126 | 2 |
| 127 | 2 |
| 128 | 2 |
| 129 | 2 |
+--------+------+
10 rows in set