ヘッダーをスキップ

Oracle Database アプリケーション開発者ガイド-基礎編
10gリリース2(10.2)

B19248-02
目次
目次
索引
索引

戻る 次へ

9 トリガーのコーディング

トリガーとは、データベース内に格納され、何かが発生したときに暗黙的に実行つまり起動されるプロシージャです。

これまでトリガーは、表またはビューに対してINSERTUPDATEまたはDELETEが発生したときのPL/SQLブロックの実行をサポートしてきました。トリガーは、DATABASEおよびSCHEMAに対するシステム・イベントおよびその他のデータ・イベントをサポートしています。Oracle Databaseでは、PL/SQLプロシージャまたはJavaプロシージャの実行もサポートします。

この章では、DMLトリガー、INSTEAD OFトリガーおよびシステム・トリガー(DATABASEおよびSCHEMAに対するトリガー)について説明します。内容は次のとおりです。

トリガーの設計

トリガーを設計するときは、次のガイドラインを使用してください。

トリガーの作成

トリガーは、CREATE TRIGGER文を使用して作成します。この文は、SQL*PlusやEnterprise Managerなどの対話型ツールで使用できます。対話型ツールを使用する場合、CREATE TRIGGER文をアクティブにするには、最終行にスラッシュ(/)を1つ付けます。

次の文は、Emp_tab表に対するトリガーを作成します。

CREATE OR REPLACE TRIGGER Print_salary_changes
  BEFORE DELETE OR INSERT OR UPDATE ON Emp_tab
  FOR EACH ROW
WHEN (new.Empno > 0)
DECLARE
    sal_diff number;
BEGIN
    sal_diff  := :new.sal  - :old.sal;
    dbms_output.put('Old salary: ' || :old.sal);
    dbms_output.put('  New salary: ' || :new.sal);
    dbms_output.put_line('  Difference ' || sal_diff);
END;
/

DML操作(INSERT文、UPDATE文およびDELETE文)が表に実行されると、トリガーが起動されます。トリガーを起動する操作の組合せは選択できます。

トリガーは、BEFOREキーワードを使用するため、表に入る前に新しい値にアクセスできます。また、:NEW.column_nameに割り当てることによって、簡単に修正されたエラーがある場合は、その値を変更できます。トリガーは、初期変更が適用され、表が一貫性のある状態に戻った後にのみ同じ表の問合せまたは変更を実行できるため、トリガーでこれらの操作をする場合は、AFTERキーワードを使用します。

トリガーは、FOR EACH ROW句を使用するため、複数行の更新または削除時などに複数回実行される場合があります。操作が発生したという事実のみを記録し、各行のデータを調べない場合は、この句を省略します。

トリガーの作成後に、次のSQL文を入力します。

UPDATE Emp_tab SET sal = sal + 500.00 WHERE deptno = 10;

このSQL文により、更新される行ごとにトリガーが1回起動され、各行に対して新しい給与、古い給与およびその差異が出力されます。

PL/SQLブロックにエラーがあると、CREATE文(またはCREATE OR REPLACE文)は正常に実行されません。


注意

トリガーのサイズは、32KB未満で指定してください。 


次の項では、トリガーの各要素の指定方法を説明します。

関連項目

CREATE TRIGGER文の例の詳細は、「トリガー・アプリケーションの例」を参照してください。 

トリガーの種類

トリガーは、ストアドPL/SQLブロックか、あるいは表、ビュー、スキーマまたはデータベース自体に関連付けられるPL/SQL、CまたはJavaのプロシージャです。Oracle Databaseでは、指定されたイベントが発生したときに、トリガーを自動的に実行します。イベントはシステム・イベントか、または表に対して発行されるDML文の形態をとります。

トリガーは次のいずれかです。

システム・イベントの概要

次のいずれかに対して起動されるトリガーを作成できます。

システム・イベント属性の取得

トリガーが起動されるときに、イベント固有の特定の属性を取得できます。

DATABASEに対してトリガーを作成するということは、トリガーになるイベントがユーザーの有効範囲の外にある(たとえば、データベースのSTARTUPおよびSHUTDOWN)ことを意味し、そのトリガーはすべてのユーザーに適用されます(たとえば、LOGONイベントに対してDBAにより作成されるトリガー)。

SCHEMAに対してトリガーを作成するということは、トリガーが現行のユーザーのスキーマ内に作成され、そのユーザーに対してのみ起動されることを意味します。

それぞれのトリガーに関して、DMLおよびシステム・イベントに対して発行を指定できます。

関連項目

「トリガーを介したシステム・イベントに対する応答」 

トリガーのネーミング

トリガーの名前は、同一スキーマ内の他のトリガーに対して一意である必要があります。他のスキーマ・オブジェクト(表、ビュー、プロシージャなど)名とは重複してもかまいません。たとえば、表とトリガーに同じ名前を付けることもできます(ただし、間違えやすいため、違う名前を付けることをお薦めします)。

トリガーが起動するタイミング

トリガーは、トリガーを実行する文に基づいて起動されます。トリガーを実行する文では次のものを指定します。

たとえば、PRINT_SALARY_CHANGESトリガーは、Emp_tab表に対してDELETEINSERTまたはUPDATEのいずれかが実行されたときに起動されます。次のいずれかの文によって、前述の例で使用されているPRINT_SALARY_CHANGESトリガーが実行されます。

DELETE FROM Emp_tab;
INSERT INTO Emp_tab VALUES ( ... );
INSERT INTO Emp_tab SELECT ... FROM ... ;
UPDATE Emp_tab SET ... ;

インポートおよびSQL*Loaderによるトリガー起動

INSERTトリガーは、SQL*Loaderによる通常のロード中に起動されます。(ダイレクト・ロードの場合、トリガーはロードの前に使用禁止になります。)

IMPコマンドのIGNOREパラメータは、インポート中にトリガーを起動するかどうかを決定します。

列リストのUPDATEトリガーに対する影響

UPDATE文が列のリストを含む場合があります。トリガーを実行する文に列リストが含まれる場合、トリガーは指定された列の1つが更新される場合にのみ起動されます。トリガーを実行する文で列リストが省略された場合、トリガーは関連付けられた表のいずれかの列が更新された場合に起動されます。INSERTまたはDELETEトリガーを実行する文に列リストを指定することはできません。

前述のPRINT_SALARY_CHANGESトリガーの例では、トリガーを実行する文に列リストを指定することができます。次に例を示します。

... BEFORE DELETE OR INSERT OR UPDATE OF ename ON Emp_tab ...

注意:

トリガー起動の制御(BEFOREオプションおよびAFTERオプション)

CREATE TRIGGER文にBEFOREまたはAFTERオプションを指定して、実行中のトリガー文によってトリガー本体が起動されるタイミングを指定できます。CREATE TRIGGER文では、トリガーを実行する文の直前にBEFOREまたはAFTERオプションを指定します。たとえば、前述の例では、PRINT_SALARY_CHANGESトリガーがBEFOREトリガーです。

一般に、BEFOREトリガーまたはAFTERトリガーは、それぞれ次のような場合に使用します。

複数回起動するBEFOREトリガー

UPDATE文またはDELETE文が同時実行中のUPDATEとの競合を検出すると、Oracle DatabaseはSAVEPOINTまでの透過的ROLLBACKを実行して、更新を再起動します。文が正常に完了するまで、これは何度も行われる可能性があります。文が再起動されるたびに、BEFORE文トリガーが再起動されます。セーブポイントまでのロールバックでは、トリガー内で参照されるパッケージ変数への変更は取り消されません。パッケージには、このような状況を検出するためのカウンタ変数を含める必要があります。

トリガーの順序

リレーショナル・データベースは、SQL文による行の処理順序を保証しません。したがって、行の処理順序に基づくトリガーは作成しないでください。たとえば、グローバル変数の現在の値が、行トリガーによって処理される行に依存する場合は、行トリガー内のグローバル・パッケージ変数に値を割り当てないでください。また、グローバル・パッケージ変数の値がトリガー内で更新される場合は、これらの変数をBEFORE文トリガー内で初期化するようにしてください。

トリガー本体内の文によって他のトリガーが起動される場合、それらのトリガーはカスケードしているといいます。 Oracle Databaseでは、一度に最大32個のトリガーをカスケードできます。なお、OPEN_CURSORS初期化パラメータを使用して、カスケード可能なトリガーの数を制限することもできます。これは、トリガーを実行するたびにカーソルがオープンされるためです。

トリガーの評価順序

トリガーは、インラインで、またはプロシージャをコールすることによって一連の操作を実行できますが、同じ型の複数のトリガーを使用すると、同じ表に対するトリガーを持つアプリケーションのインストールをモジュール化できるため、データベース管理が強化されます。

Oracle Databaseは、別の型のトリガーを実行する前に、同じ型のすべてのトリガーを実行します。1つの表に対して同じ型のトリガーが複数ある場合、Oracle Databaseでは任意の順序を選択してこれらのトリガーを実行します。

関連項目

トリガーの起動順序の詳細は、『Oracle Database概要』を参照してください。 

後続の各トリガーは、前に起動されたトリガーが変更した内容を参照します。個々のトリガーは、old値およびnew値を参照できます。old値は元の値で、new値は一番最後に起動されたUPDATEトリガーまたはINSERTトリガーが設定した現在の値です。

トリガーされた複数のアクションが特定の順序で確実に実行されるようにするには、これらのアクションをまとめて1つのトリガーに統合する必要があります(たとえば、トリガーが一連のプロシージャをコールする方法を使用します)。

複合ビューの変更(INSTEAD OFトリガー)

更新可能なビューとは、基礎となる表にDMLを実行できるビューです。元々更新可能なビューもありますが、「INSTEAD OFトリガーが必要なビュー」の項に示されている構造体の1つ以上から作成されているビューは更新できません。

前述の構造体が含まれているビューは、INSTEAD OFトリガーを使用して更新可能にできます。INSTEAD OFトリガーを使用すると、UPDATE文、INSERT文およびDELETE文では直接変更できないビューを透過的に変更できます。このようなトリガーがINSTEAD OFトリガーと呼ばれる理由は、他の種類のトリガーと異なり、Oracle Databaseはトリガーを実行する文を実行するかわりにこのトリガーを起動するためです。このトリガーは、対象となる操作を判断し、UPDATEINSERTまたはDELETE操作を基礎となる表に対して直接実行する必要があります。

INSTEAD OFトリガーを使用して、標準的なUPDATE文、INSERT文およびDELETE文をビューに対して書き込むと、正しいアクションが実行されるようにINSTEAD OFトリガーがバックグラウンドで動作します。

INSTEAD OFトリガーをアクティブにできるのは、それぞれの行に対してのみです。

関連項目

「1回または複数回のトリガーの起動(FOR EACH ROWオプション)」 


注意

  • INSTEAD OFオプションが使用できるのは、ビューに対して作成されるトリガーのみです。

  • BEFOREオプションおよびAFTERオプションは、ビューに対して作成されるトリガーには使用できません。

  • ビューのCHECKオプションは、ビューに対する挿入または更新がINSTEAD OFトリガーを使用して行われる場合は施行されません。INSTEAD OFトリガー本体でチェックを施行する必要があります。

 

INSTEAD OFトリガーが必要なビュー

ビューの問合せに次のいずれかの構造体が含まれている場合、UPDATE文、INSERT文またはDELETE文を使用してビューを変更できません。

疑似列または式が含まれたビューを更新するには、疑似列または式のどちらも参照しないUPDATE文のみを使用します。

INSTEAD OFトリガーの例


注意

次のデータ構造を設定しないと機能しない例もあります。

CREATE TABLE Project_tab (
Prj_level NUMBER,
Projno NUMBER,
Resp_dept NUMBER);
CREATE TABLE Emp_tab (
Empno NUMBER NOT NULL,
Ename VARCHAR2(10),
Job VARCHAR2(9),
Mgr NUMBER(4),
Hiredate DATE,
Sal NUMBER(7,2),
Comm NUMBER(7,2),
Deptno NUMBER(2) NOT NULL);

CREATE TABLE Dept_tab (
Deptno NUMBER(2) NOT NULL,
Dname VARCHAR2(14),
Loc VARCHAR2(13),
Mgr_no NUMBER,
Dept_type NUMBER);
 

次の例では、MANAGER_INFOビューに行を挿入するINSTEAD OFトリガーを示します。

CREATE OR REPLACE VIEW manager_info AS
    SELECT e.ename, e.empno, d.dept_type, d.deptno, p.prj_level,
           p.projno
        FROM   Emp_tab e, Dept_tab d, Project_tab p
        WHERE  e.empno =  d.mgr_no
        AND    d.deptno = p.resp_dept;

CREATE OR REPLACE TRIGGER manager_info_insert
INSTEAD OF INSERT ON manager_info
REFERENCING NEW AS n                 -- new manager information

FOR EACH ROW
DECLARE
   rowcnt number;
BEGIN
   SELECT COUNT(*) INTO rowcnt FROM Emp_tab WHERE empno = :n.empno;
   IF rowcnt = 0  THEN
       INSERT INTO Emp_tab (empno,ename) VALUES (:n.empno, :n.ename);
   ELSE
      UPDATE Emp_tab SET Emp_tab.ename = :n.ename
         WHERE Emp_tab.empno = :n.empno;
   END IF;
   SELECT COUNT(*) INTO rowcnt FROM Dept_tab WHERE deptno = :n.deptno;
   IF rowcnt = 0 THEN
      INSERT INTO Dept_tab (deptno, dept_type) 
         VALUES(:n.deptno, :n.dept_type);
   ELSE
      UPDATE Dept_tab SET Dept_tab.dept_type = :n.dept_type
         WHERE Dept_tab.deptno = :n.deptno;
   END IF;
   SELECT COUNT(*) INTO rowcnt FROM Project_tab 
      WHERE Project_tab.projno = :n.projno;
   IF rowcnt = 0 THEN
      INSERT INTO Project_tab (projno, prj_level) 
         VALUES(:n.projno, :n.prj_level);
   ELSE
      UPDATE Project_tab SET Project_tab.prj_level = :n.prj_level
         WHERE Project_tab.projno = :n.projno;
   END IF;
END;
 

MANAGER_INFOビューに行を挿入するというアクションでは、まず、MANAGER_INFOの導出元の実表に該当する行があるかどうかを調べます。その後、必要に応じて、新しい行を挿入するか、または既存の行を更新します。同じようなトリガーを使用して、UPDATEおよびDELETE用のアクションを指定できます。

オブジェクト・ビューおよびINSTEAD OFトリガー

INSTEAD OFトリガーによって、クライアント側でOCIコールを介してオブジェクト・ビューのインスタンスを変更できます。

関連項目

『Oracle Call Interfaceプログラマーズ・ガイド』 

クライアント側のオブジェクト・キャッシュ内でオブジェクト・ビューによって具体化されたオブジェクトを変更し、永続記憶域にそれをフラッシュするには、オブジェクト・ビューが変更可能なものでないかぎり、INSTEAD OFトリガーを指定する必要があります。ただし、オブジェクトが読込み専用の場合は、トリガーを定義して確保する必要はありません。

ネストした表のビューの列に対するトリガー

INSTEAD OFトリガーは、ネストした表のビューの列に対しても作成できます。このトリガーは、ネストした表の要素を更新する手段を提供します。このトリガーは、ネストした表の各更新対象要素に対して起動されます。トリガー内の行相関変数が、ネストした表の要素に対応します。この種のトリガーは、変更対象のネストした表を含む親である行にアクセスするための追加相関名も提供します。


注意

このトリガーの特長は次のとおりです。

  • ビューの中のネストした表の列に対してのみ定義できます。

  • ネストした表の要素が、THE()句またはTABLE()句を使用して変更されるときにのみ起動されます。ビューに対してDML文が実行されるときには起動されません。

 

たとえば、社員用のネストした表を含む部門ビューについて考えてみます。

CREATE OR REPLACE VIEW Dept_view AS
SELECT d.Deptno, d.Dept_type, d.Dept_name,
   CAST (MULTISET ( SELECT e.Empno, e.Empname, e.Salary)
      FROM Emp_tab e
      WHERE e.Deptno = d.Deptno) AS Amp_list_ Emplist
FROM Dept_tab d;

CAST(MULTISET..)演算子によって、部門ごとに社員の多重集合が作成されます。社員のネストした表であるemplist列を変更する場合は、この列に対してINSTEAD OF トリガーを定義して処理できます。

次の例は、挿入トリガーの作成方法を示します。

CREATE OR REPLACE TRIGGER Dept_emplist_tr
   INSTEAD OF INSERT ON NESTED TABLE Emplist OF Dept_view
   REFERENCING NEW AS Employee
      PARENT AS Department
   FOR EACH ROW
BEGIN
-- The insert on the nested table is translated to an insert on the base table:
   INSERT INTO Emp_tab VALUES (
      :Employee.Empno, :Employee.Empname,:Employee.Salary, :Department.Deptno);
END;

ネストした表にINSERTが実行されるとトリガーが起動され、Emp_tab表が正しい値で埋められます。次に例を示します。

INSERT INTO TABLE (SELECT d.Emplist FROM Dept_view d WHERE Deptno = 10)
   VALUES (1001, 'John Glenn', 10000);

この例の:department.deptno相関変数には、値10が入ります。

1回または複数回のトリガーの起動(FOR EACH ROWオプション)

FOR EACH ROWオプションによって、トリガーが行トリガーになるか文トリガーになるかが決定されます。FOR EACH ROWを指定すると、トリガーを実行する文によって影響を受ける表の各行に対してトリガーが1回起動されます。FOR EACH ROWオプションを指定しないと、トリガーは該当する個々の文に対して1回のみ起動され、その文によって影響される各行に対して別々に起動されることはありません。

たとえば、次のようなトリガーを定義します。


注意

次のようなデータ構造を設定しないと機能しない例もあります。

CREATE TABLE Emp_log (
Emp_id NUMBER,
Log_date DATE,
New_salary NUMBER,
Action VARCHAR2(20));
 

CREATE OR REPLACE TRIGGER Log_salary_increase
AFTER UPDATE ON Emp_tab
FOR EACH ROW
WHEN (new.Sal > 1000)
BEGIN
    INSERT INTO Emp_log (Emp_id, Log_date, New_salary, Action)
       VALUES (:new.Empno, SYSDATE, :new.SAL, 'NEW SAL');
END;

次に、次のSQL文を入力します。

UPDATE Emp_tab SET Sal = Sal + 1000.0
    WHERE Deptno = 20;

部門20に5人の社員がいる場合、この文が入力されるとトリガーが5回起動されます。これは、5つの行が影響を受けるためです。

次のトリガーは、Emp_tab表の各UPDATEに対して1回のみ起動します。

CREATE OR REPLACE TRIGGER Log_emp_update
AFTER UPDATE ON Emp_tab
BEGIN
    INSERT INTO Emp_log (Log_date, Action)
        VALUES (SYSDATE, 'Emp_tab COMMISSIONS CHANGED');
END;

関連項目

トリガーの起動順序の詳細は、『Oracle Database概要』を参照してください。 

文レベル・トリガーは、文全体の妥当性チェックを実行するときに有効です。

条件に基づいたトリガーの起動(WHEN句)

行トリガー定義にトリガー制約をオプションで指定できます。これには、WHEN句にSQLのブール式を指定します。


注意

WHEN句は、文トリガーの定義に含めることはできません。 


このオプションを指定すると、WHEN句の式がトリガーの処理対象となる行ごとに評価されます。

行に対して式がTRUEと評価されると、その行のかわりにトリガー本体が起動されます。ただし、行に対して式がFALSEまたはNOT TRUEと評価された場合(NULLの場合のように不明な場合)、その行に対してトリガー本体は起動されません。WHEN句の評価は、トリガーを実行するSQL文の実行には影響しません(WHEN句の式がFALSEと評価されても、トリガーを実行する文はロールバックされません)。

たとえば、PRINT_SALARY_CHANGESトリガーでは、Empnoの新しい値が0(ゼロ)、NULLまたは負の場合、トリガー本体は実行されません。より具体的な例としては、ある列の値が他の列の値より小さいかどうかテストする場合があります。

行トリガーのWHEN句の式に相関名を指定できます。相関名については後述します。WHEN句の式はSQL式にする必要があり、副問合せを含むことはできません。WHEN句では、PL/SQL式(ユーザー定義ファンクションを含む)は使用できません。


注意

WHEN句はINSTEAD OFトリガーには指定できません。 


トリガー本体のコーディング

トリガー本体は、SQL文またはPL/SQL文を含めることができるCALLプロシージャまたはPL/SQLブロックです。CALLプロシージャは、PL/SQLまたはPL/SQLラッパーにカプセル化されたJavaプロシージャのどちらかです。これらの文は、トリガーを実行する文が入力され、トリガー制約(含まれている場合)がTRUEと評価された場合に実行されます。

行トリガーのトリガー本体には、相関名、REFERENCEINGオプション、条件述語のINSERTINGDELETINGUPDATINGなどの特殊な要素を含められます。これらの要素は、PL/SQLブロックのコードにも含めることができます。


注意

INSERTINGDELETINGUPDATING条件述語は、CALLプロシージャには使用できません。使用できるのはPL/SQLブロック内のみです。 


例: トリガーを使用したログインの監視


注意

次のようなデータ構造を設定しないと機能しない例もあります。

CONNECT system/manager
GRANT ADMINISTER DATABASE TRIGGER TO scott;
CONNECT scott/tiger
CREATE TABLE audit_table (
seq number,
user_at VARCHAR2(10),
time_now DATE,
term VARCHAR2(10),
job VARCHAR2(10),
proc VARCHAR2(10),
enum NUMBER);
 

CREATE OR REPLACE PROCEDURE foo (c VARCHAR2) AS 
   BEGIN 
      INSERT INTO Audit_table (user_at) VALUES(c);
   END;

CREATE OR REPLACE TRIGGER logontrig AFTER LOGON ON DATABASE 
-- Just call an existing procedure. The ORA_LOGIN_USER is a function
-- that returns information about the event that fired the trigger.
CALL foo (ora_login_user)
/
例: トリガーからのJavaプロシージャのコール

トリガーは、PL/SQLで宣言されますが、Javaなどの他の言語でプロシージャをコールすることができます。

CREATE OR REPLACE PROCEDURE Before_delete (Id IN NUMBER, Ename VARCHAR2)
IS language Java
name 'thjvTriggers.beforeDelete (oracle.sql.NUMBER, oracle.sql.CHAR)';

CREATE OR REPLACE TRIGGER Pre_del_trigger BEFORE DELETE ON Tab 
FOR EACH ROW
CALL Before_delete (:old.Id, :old.Ename)
/

対応するJavaファイルはthjvTriggers.javaです。

import java.sql.*
import java.io.*
import oracle.sql.*
import oracle.oracore.*
public class thjvTriggers
{
public state void
beforeDelete (NUMBER old_id, CHAR old_name)
Throws SQLException, CoreException
   {
   Connection conn = JDBCConnection.defaultConnection();
   Statement stmt = conn.CreateStatement();
   String sql = "insert into logtab values
   ("+ old_id.intValue() +", '"+ old_ename.toString() + ", BEFORE DELETE');
   stmt.executeUpdate (sql);
   stmt.close();
   return;
   }
}

行トリガーでの列値のアクセス

行トリガーのトリガー本体では、PL/SQLコードおよびSQL文は、トリガーを実行する文によって影響を受ける現在の行に含まれるold列値およびnew列値にアクセスできます。変更される表の各列に2つの相関名(old列値用およびnew列値用に1つずつ)があります。トリガーを実行する文の種類によっては、相関名が意味を持たない場合もあります。

元の列値は、列名の前にold修飾子を指定して参照し、新しい列値は列名の前にnew修飾子を指定して参照します。たとえば、トリガーを実行する文がEmp_tab表(列SALCOMMなどを持つ)に対応付けられている場合、トリガー本体に文を含めることができます。次に例を示します。

IF :new.Sal > 10000 ...
IF :new.Sal < :old.Sal ...

old値およびnew値は、BEFOREおよびAFTER行トリガー内で使用できます。new列値はBEFORE行トリガー内に割り当てることができますが、(AFTER行トリガーが起動される前にトリガーを実行する文が有効となるため)AFTER行トリガーにnew列値を割り当てることはできません。BEFORE行トリガーによってnew.columnの値が変更されると、同じ文によって起動されるAFTER行トリガーは、BEFORE行トリガーによって割り当てられた変更を参照します。

WHEN句のブール式には相関名を使用することもできます。oldおよびnew修飾子をトリガー本体で使用する場合は、修飾子の前にコロン(:)を付ける必要があります。ただし、修飾子をWHEN句またはREFERENCINGオプションで使用する場合、コロンは使用できません。

例: トリガーによるLOB列の変更

通常のSQLおよびCLOB列を持つPL/SQLファンクションを使用して、LOB列を他の列と同様に処理できます。また、BLOB列を持つDBMS_LOBパッケージをコールできます。

drop table tab1;

create table tab1 (c1 clob);
insert into tab1 values ('<h1>HTML Document Fragment</h1><p>Some text.');

create or replace trigger trg1
  before update on tab1
  for each row
begin
  dbms_output.put_line('Old value of CLOB column: '||:OLD.c1);
  dbms_output.put_line('Proposed new value of CLOB column: '||:NEW.c1);

-- Previously, we couldn't change the new value for a LOB.
-- Now, we can replace it, or construct a new value using SUBSTR, INSTR...
-- operations for a CLOB, or DBMS_LOB calls for a BLOB.
  :NEW.c1 := :NEW.c1 || to_clob('<hr><p>Standard footer paragraph.');

  dbms_output.put_line('Final value of CLOB column: '||:NEW.c1);
end;
/ 

set serveroutput on;
update tab1 set c1 = '<h1>Different Document Fragment</h1><p>Different text.';

select * from tab1;

ネストした表のビューの列に対するINSTEAD OFトリガー

ネストした表のビューの列に対するINSTEAD OFトリガーの場合は、newおよびold修飾子が、ネストした表の新しい要素および古い要素に対応します。このネストした表の要素に対応する親である行は、parent修飾子を使用してアクセスできます。親相関名は、ネストした表のトリガー内でのみ意味があり有効です。

トリガーとの名前の競合の回避(REFERENCINGオプション)

行トリガーのトリガー本体にREFERENCINGオプションを指定して、oldまたはnewとネーミングされる相関名または表の重複を避けることができます。ただし、このようなことはほとんど発生しないため、このオプションはほとんど使用されません。

たとえば、field1(数値)およびfield2(文字)の列を含むnew表があるとします。次のCREATE TRIGGERの例は、相関名を指定できる表newに対応付けられるトリガーの例です。相関名と表名の重複が避けられています。


注意

次のようなデータ構造を設定しないと機能しない例もあります。

CREATE TABLE new (
field1 NUMBER,
field2 VARCHAR2(20));
 

CREATE OR REPLACE TRIGGER Print_salary_changes
BEFORE UPDATE ON new
REFERENCING new AS Newest
FOR EACH ROW
BEGIN
   :Newest.Field2 := TO_CHAR (:newest.field1);
END;

REFERENCINGオプションを使用してnew修飾子をnewestに名前を変更し、その後でトリガー本体に使用していることに注意してください。

トリガーを起動するDML操作の検出

種類が異なる複数のDML操作でトリガーを起動する場合(たとえば、ON INSERT OR DELETE OR UPDATE OF Emp_tab)は、トリガー本体で条件述語のINSERTINGDELETINGおよびUPDATINGを使用して、トリガーを起動する文の種類の確認ができます。

トリガー本体のコード内で、トリガーを起動するDML操作の種類に応じて、次のコード・ブロックを実行できます。

IF INSERTING THEN ... END IF;
IF UPDATING THEN ... END IF;

最初の条件は、トリガーを起動した文がINSERT文の場合にのみTRUEと評価されます。2番目の条件は、トリガーを起動した文がUPDATE文の場合にのみTRUEと評価されます。

UPDATEトリガーでは、UPDATING条件述語に列名を指定して、指定した列が更新されているかどうかを判断できます。たとえば、トリガーが次のように定義されているとします。

CREATE OR REPLACE TRIGGER ...
... UPDATE OF Sal, Comm ON Emp_tab ...
BEGIN

... IF UPDATING ('SAL') THEN ... END IF;

END;

THEN句のコードは、トリガーUPDATE文がSAL列を更新する場合にのみ実行されます。このように、対象の列が変更されていない場合は、トリガーはオーバーヘッドを最小化できます。

トリガー本体内のエラー条件および例外

トリガー本体の実行中に、事前定義またはユーザー定義のエラー条件または例外が発生すると、トリガーを実行する文のみでなくトリガー本体のすべての影響が(エラーが例外ハンドラによって検出された場合を除き)ロールバックされます。したがって、トリガー本体は例外を発生させることによって、トリガーを実行する文を実行しないで済みます。ユーザー定義例外は、複雑なセキュリティ認可または整合性制約を施行するトリガーによく使用されます。

これに対する唯一の例外は、対象イベントがデータベースのSTARTUPSHUTDOWN、またはログインしているユーザーがSYSTEMのときのLOGINの場合です。このような場合は、トリガー・アクションのみがロールバックされます。

オブジェクト表に対するトリガー

10gリリース1(10.1)以降、オブジェクト表に対するトリガーでは、OBJECT_VALUE疑似列を使用できます。OBJECT_VALUEは、オブジェクト全体を意味します。これは1つの使用例です。また、IN仮パラメータのデータ型として、OBJECT_VALUEを指定したPL/SQLファンクションもコールできます。

次に、トリガー内でのOBJECT_VALUEの使用例を示します。また、オブジェクト表tbl内の値に対する更新を追跡するために、次の例では、履歴表であるtbl_historyが作成されます。tblの場合、1〜5までの値がnに挿入されます。mは常時0です。トリガーは、DML文によって影響を受ける各行に対して1回実行される行レベルのトリガーです。tblが更新されると、トリガーにより、tbl内のオブジェクトtのold値およびnew値がtbl_historyに書き込まれます。old値およびnew値は、:OLD.OBJECT_VALUEおよび:NEW.OBJECT_VALUEです。表tblの更新が行われます(各n値が1ずつ増加します)。トリガーが動作していることを確認するために、履歴表からの選択内容が、例の終わりに示されます。

CREATE OR REPLACE TYPE t AS OBJECT (n NUMBER, m NUMBER)
/
CREATE TABLE tbl OF t
/
BEGIN
  FOR j IN 1..5 LOOP
    INSERT INTO tbl VALUES (t(j, 0));
  END LOOP;
END;
/
CREATE TABLE tbl_history ( d DATE, old_obj t, new_obj t)
/
CREATE OR REPLACE TRIGGER Tbl_Trg
AFTER UPDATE ON tbl
FOR EACH ROW
BEGIN
  INSERT INTO tbl_history (d, old_obj, new_obj)
    VALUES (SYSDATE, :OLD.OBJECT_VALUE, :NEW.OBJECT_VALUE);
END Tbl_Trg;
/
--------------------------------------------------------------------------------
 
UPDATE tbl SET tbl.n = tbl.n+1
/
BEGIN
  FOR j IN (SELECT d, old_obj, new_obj FROM tbl_history) LOOP
    Dbms_Output.Put_Line (
      j.d||
      ' -- old: '||j.old_obj.n||' '||j.old_obj.m||
      ' -- new: '||j.new_obj.n||' '||j.new_obj.m);
  END LOOP;
END;
/

選択結果は、列nの値がすべて1ずつ増加していることを示しています。mの値は0のままです。選択結果は次のとおりです。

23-MAY-05 -- old: 1 0 -- new: 2 0
23-MAY-05 -- old: 2 0 -- new: 3 0
23-MAY-05 -- old: 3 0 -- new: 4 0
23-MAY-05 -- old: 4 0 -- new: 5 0
23-MAY-05 -- old: 5 0 -- new: 6 0

トリガーおよびリモート例外処理

リモート・サイトにアクセスするトリガーは、ネットワーク・リンクが使用できない場合はリモート例外処理を実行できません。次に例を示します。

CREATE OR REPLACE TRIGGER Example
AFTER INSERT ON Emp_tab
FOR EACH ROW
BEGIN
  INSERT INTO Emp_tab@Remote     -- <- compilation fails here
  VALUES ('x');                  --    when dblink is inaccessible
EXCEPTION
  WHEN OTHERS THEN
    INSERT INTO Emp_log
    VALUES ('x');
END;

トリガーは作成されたときにコンパイルされます。したがって、トリガーをコンパイルする必要があるときにリモート・サイトを使用できないと、Oracle Databaseはリモート・データベースにアクセスする文の妥当性チェックができず、コンパイルは正常に実行されません。前述の例外文の例は、トリガーがコンパイルを完了しないため実行できません。

ストアド・プロシージャはコンパイル済の形式で格納されるため、前述の例の回避策は次のようになります。

CREATE OR REPLACE TRIGGER Example
AFTER INSERT ON Emp_tab
FOR EACH ROW
BEGIN
   Insert_row_proc;
END;

CREATE OR REPLACE PROCEDURE Insert_row_proc AS
BEGIN
    INSERT INTO Emp_tab@Remote
    VALUES ('x');
EXCEPTION
   WHEN OTHERS THEN
       INSERT INTO Emp_log
       VALUES ('x');
END;

この例のトリガーは正常にコンパイルし、ストアド・プロシージャをコールします。このストアド・プロシージャには、リモート・データベースにアクセスするための妥当性チェック済の文がすでにあります。したがって、リンクが使用できないためにリモートINSERT文が失敗すると、例外が捕捉されます。

トリガー作成の制限

トリガーのコーディングには、標準PL/SQLブロックにはない、いくつかの制約があります。次の項では、トリガーのこのような制約を説明します。

トリガーの最大サイズ

トリガーのサイズは、32KB以下に指定する必要があります。

トリガー本体で使用可能なSQL文

トリガー本体には、DML SQL文を含めることができます。 また、SELECT文を含めることはできますが、SELECT... INTO...文またはカーソル定義内のSELECT文を指定する必要があります。

DDL文はトリガー本体には含めることはできません。また、トランザクション制御文もトリガーには含めることはできません。ROLLBACKCOMMITおよびSAVEPOINTは使用できません。システム・トリガーの場合は、{CREATE/ALTER/DROP} TABLE文およびALTER...COMPILEを使用できます。


注意

トリガーによってコールされるプロシージャは、トリガー本体のコンテキスト内で実行されるため、このようなプロシージャが前述のトランザクション制御文を実行することはできません。 


トリガー内の文では、リモート・スキーマ・オブジェクトを参照できます。ただし、ローカル・トリガー内からリモート・プロシージャをコールするときは、特に注意が必要です。トリガーの実行中にタイムスタンプまたはシグネチャの不一致が見つかると、リモート・プロシージャは実行されず、トリガーが無効になります。

LONGデータ型およびLONG RAWデータ型に関するトリガー制限

トリガー内のLONGおよびLONG RAWデータ型には、次の制限があります。

変更表のトリガー制限

変更表とは、UPDATE文、DELETE文、INSERT文で現在変更されている表、またはDELETE CASCADE制約の影響によって更新される可能性のある表のことです。

トリガーを実行する文を発行したセッションは、変更表を問合せまたは変更できません。この制限によって、トリガーは一貫性のないデータは参照しません。

この制限は、FOR EACH ROW句を使用するすべてのトリガーに適用されます。INSTEAD OFトリガー内で変更中のビューは、変更ビューとみなされません。

変更表でトリガーが起動されると、ランタイム・エラーが発生し、トリガー本体の処理結果およびトリガーを実行する文がロールバックされ、ユーザーまたはアプリケーションに制御が戻ります。

次のトリガーについて検討してみます。

CREATE OR REPLACE TRIGGER Emp_count
AFTER DELETE ON Emp_tab
FOR EACH ROW
DECLARE
    n INTEGER;
BEGIN
    SELECT COUNT(*) INTO n FROM Emp_tab;
    DBMS_OUTPUT.PUT_LINE(' There are now ' || n ||
        ' employees.');
END;

次のSQL文が入力されるとします。

DELETE FROM Emp_tab WHERE Empno = 7499;

行が削除されるときに表が変更中であるため、次のエラーが戻されます。

ORA-04091:表SCOTT.Emp_tabは変更しています。トリガー/関数は見ることができません

トリガーからFOR EACH ROW行を削除すると、このトリガーは文トリガーになり、制限やトリガーの対象にはなりません。

変更表を更新する必要がある場合、一時表、PL/SQL表またはパッケージ変数を使用してこれらの制限を回避することもできます。たとえば、元の表を更新する1つのAFTER行トリガーが変更表エラーとなった場合、かわりに、一時表を更新するAFTER行トリガーおよび一時表からの値を使用して元の表を更新するAFTER文トリガーの2つのトリガーを使用できる場合があります。

宣言整合性制約は、行トリガーに関して随時テストされます。

関連項目

トリガー間の相互作用と整合性制約の詳細は、『Oracle Database概要』 を参照してください。 

分散データベースの異なるノードの表の間では、宣言参照整合性制約はサポートされていないため、変更表の制限は、リモート・ノードにアクセスするトリガーには適用されません。これらの制限は、ループバック・データベース・リンクで接続されている同一データベース内の表の間でも施行されません。ループバック・データベース・リンクでは、リンクを含むデータベースに戻るOracle Netパスを定義して、ローカル表がリモートで表示されます。

変更表の制約の緩和

この項の前半で説明したように、変更エラーが存在するため、親文が変更する表をトリガーが読込みまたは変更を行うことはできません。 ただし、Oracle Databaseリリース8.1以上では、親表に対して削除を行うと、BEFORE/AFTER文トリガーが1回起動されます。これによって、(行トリガー以外の)トリガーを作成して親表および子表の読込みおよび変更を行うことができます。

これによって、ほとんどの外部キー制約アクションはそれらの明白なAFTER行トリガーを経由して実装されるため、制約は自己参照的ではなくなります。更新カスケード、更新セットNULL、更新セット・デフォルト、削除セット・デフォルト、欠落した親の挿入および子件数メンテナンスは、すべて簡単に実装できます。次に、更新カスケードの実装の例を示します。

  create table p (p1 number constraint ppk primary key); 
  create table f (f1 number constraint ffk references p); 
  create trigger pt after update on p for each row begin 
    update f set f1 = :new.p1 where f1 = :old.p1; 
  end; 
  / 
 

この実装の場合、複数行を更新するときに注意が必要です。たとえば、表pに値(1)、(2)、(3)を持つ3つの行があり、表fにも値(1)、(2)、(3)を持つ3つの行があるとすると、次の文はpを正常に更新しますが、トリガーがfを更新するときに問題が発生します。

  update p set p1 = p1+1; 
 

まず、この文はpの値(1)から(2)への更新を行い、トリガーはfの値(1)から(2)への更新を行い、fに値(2)の2つの行を残します。次に、文はpの値(2)から(3)への更新を行い、トリガーはfの2つの行の値を両方とも(2)から(3)へ更新します。最後に、文はpの値(3)から(4)への更新を行い、トリガーはfの3つの行すべてを(3)から(4)へ更新します。pとfのデータの関連は失われます。

この問題を回避するため、主キーを変更するpの複数行更新を禁止し、既存の主キー値を再利用する必要があります。また、すでに更新された外部キー値を追跡し、どの行も2回更新されないようにトリガーを変更することで解決することもできます。

これが、外部キーの更新に関するこの方法の唯一の問題です。トリガーは、別のトランザクションによってコミットされていない変更済の行を見逃すことはありません。これは、AFTER行トリガーがコールされるまでは、いずれの一致する外部キー行もロックされないことを外部キー制約が保証するためです。

システム・トリガーの制限

イベントの違いによって、様々なイベント属性関数が使用できます。たとえば、特定のDDL操作をDDLイベントに対して使用できない場合があります。イベント属性関数は、エラー条件を作成するのではなく、定義されない場合があるため、イベント属性関数を使用する前に、「イベント属性関数」を確認してください。

コミットされたトリガーのみが起動されます。たとえば、すべてのCREATEイベントの後に起動されるトリガーを作成した場合、このトリガーはそのトリガー自身の作成後には起動されません。これは、CREATEイベントが起動された時点では、このトリガーに関する正しい情報はまだコミットされていないためです。

たとえば、次のSQL文を実行するとします。

CREATE OR REPLACE TRIGGER my_trigger AFTER CREATE ON DATABASE 
BEGIN null; 
END; 

トリガーmy_triggerは、my_triggerの作成後には起動されません。Oracle Databaseでは、コミットされていないトリガーは起動されません。

外部関数のコールアウト

外部関数のコールアウトに関するすべての制限も適用されます。

トリガー・ユーザーとは

次の文では、トリガーには、トリガーの所有者の名前は戻されますが、表を更新しているユーザーの名前は戻されません。

SELECT Username FROM USER_USERS;

トリガーの使用に必要な権限

ご使用のスキーマに対してトリガーを作成するには、CREATE TRIGGERシステム権限および次のいずれかが必要です。

他のユーザーのスキーマ内にトリガーを作成、または自スキーマ内のトリガーから他のスキーマ内の表を参照するには、CREATE ANY TRIGGERシステム権限が必要です。この権限があると、任意のスキーマ内にトリガーを作成し、任意のユーザーの表と対応付けることができます。さらに、トリガーを作成するユーザーには、参照するプロシージャ、ファンクションまたはパッケージに対するEXECUTE権限も必要です。

DATABASEに対してトリガーを作成するには、ADMINISTER DATABASE TRIGGER権限が必要です。この権限が後になって取り消された場合、トリガーを削除することはできますが、変更することはできません。

トリガー本体で参照されるスキーマ・オブジェクトへのオブジェクト権限は、トリガーの所有者に(ロールを介さずに)明示的に付与する必要があります。トリガー本体の文は、トリガーを実行する文を発行するユーザーの権限ドメインではなく、そのトリガーの所有者の権限ドメインから操作します。これは、ストアド・プロシージャの権限モデルと類似しています。

トリガーのコンパイル

トリガーは、無名PL/SQLブロックに:newおよび:old機能を追加したものと類似していますが、コンパイル方法が異なります。無名PL/SQLブロックは、メモリーにロードされると、常に、コンパイルされます。コンパイルには、次の3段階が必要です。

  1. 構文検査: PL/SQL構文がチェックされ、解析ツリーが生成されます。

  2. セマンティクス・チェック: タイプ・チェックおよび解析ツリーに対する追加処理が行われます。

  3. コード生成: pcodeが生成されます。

これに対して、トリガーは、CREATE TRIGGER文が入力されたときに完全にコンパイルされ、pcodeはデータ・ディクショナリに格納されます。そのため、トリガーを起動するときに、共有カーソルをオープンしてトリガー・アクションを実行する必要はなくなります。そのかわり、トリガーは直接実行されます。

トリガーのコンパイル中にエラーが発生しても、トリガーは作成されます。ただし、DML文がこのトリガーを起動すると、そのDML文は失敗します。(ランタイム・トリガー・エラーが発生すると、DML文は必ず失敗します。)トリガーの作成時にすべてのコンパイル・エラーが表示されるように、SQL*PlusまたはEnterprise Manager内でSHOW ERRORS文を使用するか、またはUSER_ERRORSビューからエラーをSELECTすることができます。

トリガーの依存関係

コンパイル済のトリガーには依存関係があります。このようなトリガーは、トリガー本体からコールされるファンクションまたはストアド・プロシージャのような依存対象となるオブジェクトが修正されると無効になります。依存性の理由で無効になったトリガーは、次に起動された時点で再コンパイルされます。

ALL_DEPENDENCIESビューを調べると、トリガーの依存関係がわかります。たとえば、次の文は、SCOTTスキーマ内のトリガーの依存関係を示します。

SELECT NAME, REFERENCED_OWNER, REFERENCED_NAME, REFERENCED_TYPE
    FROM ALL_DEPENDENCIES
    WHERE OWNER = 'SCOTT' and TYPE = 'TRIGGER';

トリガーは他のファンクションまたはパッケージに依存することがあります。トリガー内に指定されているファンクションまたはパッケージが削除されると、トリガーは無効とマークされます。イベントの発生時点で、トリガーの有効性が妥当性チェックされます。トリガーが有効でない場合は、VALID WITH ERRORSとマークされ、イベントが失敗します。


注意

  • STARTUPイベントに関しては例外が1つあります。STARTUPイベントは、トリガーが失敗しても正常に実行されます。SYSTEMとしてログインした場合は、SHUTDOWNイベントおよびLOGONイベントに関しても例外があります。

  • メッセージのエンキューにはDBMS_AQパッケージが使用されるため、トリガーとキューの間の依存性は維持されません。

 

トリガーの再コンパイル

トリガーを手動で再コンパイルするには、ALTER TRIGGERコマンドを使用します。たとえば、次の文はPRINT_SALARY_CHANGESトリガーを再コンパイルします。

ALTER TRIGGER Print_salary_changes COMPILE;

トリガーを再コンパイルするには、トリガーを所有しているか、またはALTER ANY TRIGGERシステム権限が必要です。

トリガーの変更

ストアド・プロシージャと同様に、トリガーは明示的に変更することはできません。つまり、新しい定義と置き換える必要があります。(ALTER TRIGGER文は、トリガーを再コンパイルするか、使用可能にするかまたは使用禁止にするためにのみ使用します。)

トリガーを置き換えるときは、CREATE TRIGGER文にOR REPLACEオプションを指定する必要があります。OR REPLACEオプションを使用することによって、元のトリガーに付与された権限に影響せずに、古いトリガーを新しいトリガーに置き換えることができます。

また、トリガーはDROP TRIGGER文を使用して削除でき、削除してからCREATE TRIGGER文を再実行できます。

トリガーを削除するには、トリガーが自スキーマ内にあるか、またはDROP ANY TRIGGERシステム権限が必要です。

トリガーのデバッグ

ストアド・プロシージャで使用できる機能と同じ機能を使用して、トリガーをデバッグできます。

関連項目

「ストアド・プロシージャのデバッグ」 

トリガーの使用可能および使用禁止

トリガーは、次の2つのモードのどちらかです。

使用可能:トリガーを実行する文が入力され、トリガー制限が(存在する場合に)TRUEと評価された場合、使用可能トリガーによってトリガー本体が実行されます。

使用禁止:トリガーを実行する文が入力され、トリガー制限が(存在する場合に)TRUEと評価された場合でも、使用禁止トリガーはトリガー本体を実行しません。

トリガーの使用可能

デフォルトでは、トリガーは、作成時に自動的に使用可能に設定されます。ただし、トリガーは後で使用禁止にできます。トリガーを使用禁止にする必要がある作業を終了した後は、トリガーが適切なときに起動されるように、再び使用可能にしておきます。

使用禁止にしたトリガーを使用可能にするには、ALTER TRIGGER文のENABLEオプションを使用します。INVENTORY表の使用禁止にされたREORDERトリガーを使用可能にするには、次のように入力します。

ALTER TRIGGER Reorder ENABLE;

ALTER TABLE文のENABLE句にALL TRIGGERSオプションを使用すると、1つの文で、ある表に定義されているすべてのトリガーを使用可能にできます。たとえば、INVENTORY表に定義されているすべてのトリガーを使用可能にするには、次のように指定します。

ALTER TABLE Inventory
    ENABLE ALL TRIGGERS;

トリガーの使用禁止

次のような場合、一時的にトリガーを使用禁止にできます。

デフォルトでは、トリガーは作成時に使用可能に設定されます。トリガーを使用禁止にするには、ALTER TRIGGER文のDISABLEオプションを使用します。

たとえば、INVENTORY表のREORDERトリガーを使用禁止にするには、次のように指定します。

ALTER TRIGGER Reorder DISABLE;

ALTER TABLE文のDISABLE句にALL TRIGGERSオプションを使用すると、1つの文で、ある表に対応づけられたすべてのトリガーを使用禁止にできます。たとえば、INVENTORY表に定義されているすべてのトリガーを使用禁止にするには、次のように指定します。

ALTER TABLE Inventory
    DISABLE ALL TRIGGERS;

トリガーに関する情報のリスト

次のデータ・ディクショナリ・ビューには、トリガーに関する情報が表示されます。

新しい列BASE_OBJECT_TYPEは、トリガーがDATABASESCHEMA、表またはビューのどれに基づくかを示します。基になるオブジェクトが表またはビューでない場合は、古い列TABLE_NAMEがNULLです。

ACTION_TYPE列は、トリガーがコール型のトリガーかPL/SQLトリガーかを示します。

TRIGGER_TYPE列には、システム・イベントにのみ適用される他の2つの値、BEFORE EVENTおよびAFTER EVENTが含まれます。

TRIGGERING_EVENT列には、システム・イベントおよびDMLイベントがすべて含まれます。

関連項目

これらのデータ・ディクショナリ・ビューの詳細は、『Oracle Databaseリファレンス』を参照してください。 

たとえば、REORDERトリガーを作成する次の文を考えます。


注意

次のデータ構造を設定しないと機能しない例もあります。 


CREATE OR REPLACE TRIGGER Reorder
AFTER UPDATE OF Parts_on_hand ON Inventory
FOR EACH ROW
WHEN(new.Parts_on_hand < new.Reorder_point)
DECLARE
   x NUMBER;
BEGIN
   SELECT COUNT(*) INTO x
      FROM Pending_orders
      WHERE Part_no = :new.Part_no;
   IF x = 0  THEN
      INSERT INTO Pending_orders
         VALUES (:new.Part_no, :new.Reorder_quantity,
                 sysdate);
   END IF;
END;

次の2つの問合せは、REORDERトリガーに関する情報を戻します。

SELECT Trigger_type, Triggering_event, Table_name
   FROM USER_TRIGGERS
   WHERE Trigger_name = 'REORDER';

TYPE             TRIGGERING_STATEMENT       TABLE_NAME
---------------- -------------------------- ------------
AFTER EACH ROW   UPDATE                     INVENTORY

SELECT Trigger_body
   FROM USER_TRIGGERS
   WHERE Trigger_name = 'REORDER';

TRIGGER_BODY
--------------------------------------------
DECLARE
   x NUMBER;
BEGIN
   SELECT COUNT(*) INTO x
      FROM Pending_orders
      WHERE Part_no = :new.Part_no;
   IF x = 0
      THEN INSERT INTO Pending_orders
         VALUES (:new.Part_no, :new.Reorder_quantity,
            sysdate);
   END IF;
END;

トリガー・アプリケーションの例

トリガーを使用して、様々な方法でOracle Databaseの情報管理をカスタマイズできます。一般に、トリガーは次の用途に使用します。

この項では、これらのトリガー・アプリケーションの例を紹介します。これらの例をそのまま使用することはできませんが、トリガーを設計するときの参考にしてください。

トリガーを使用した監査: 例

トリガーは、Oracle Databaseの組込み監査機能を補うためによく使用されます。トリガーを作成して、AUDITコマンドによって記録される情報と同様の情報を記録することはできますが、トリガーは、より詳細な監査情報が必要な場合に使用します。たとえば、トリガーを使用すると、行単位の値に基づく監査が可能です。

AUDIT文が、機密保護監査機能と考えられていることに対して、トリガーは、財務監査機能を提供します。

データベース・アクティビティを監査するトリガーを作成するかどうかを判断するときは、表9-1に示されているように、トリガーで定義される監査に比べてOracle Databaseの監査機能で何が提供されるかを検討します。

表 9-1    組込み監査とトリガー・ベースの監査の比較 
監査機能  説明 

DMLおよびDDLの監査 

標準監査オプションによって、すべてのタイプのスキーマ・オブジェクトと構造体に関するDML文とDDL文の監査が可能です。これに比べて、トリガーでは、表に対して入力されたDML文の監査と、SCHEMAまたはDATABASEレベルでのDDL監査ができます。 

集中監査証跡 

すべてのデータベース監査情報は、Oracle Databaseの監査機能によって自動的、集中的に記録されます。 

宣言方式 

トリガーで定義された監査機能と比べ、Oracle Databaseの標準機能で使用可能になる監査機能は宣言およびメンテナンスが簡単で、エラーが発生しにくくなります。 

監査オプションの監査 

既存の監査オプションの変更を監査して、不当なデータベース・アクティビティを防止できます。 

セッションおよび実行時の監査 

データベース監査機能を使用して、監査文が入力されるたびに(BY ACCESS)、または監査文を入力するセッションごとに(BY SESSION)、レコードを生成できます。トリガーではセッション単位の監査はできません。監査レコードは、トリガーで監査される表が参照されるたびに生成されます。 

失敗したデータ・アクセスの監査 

データ・アクセスがエラーとなった場合、データベース監査を実施するように設定できます。ただし、自律型トランザクションが使用されないかぎり、トリガーを実行する文がロールバックされると、トリガーによって生成された監査情報もロールバックされます。自律型トランザクションの詳細は、『Oracle Database概要』を参照してください。 

セッションの監査 

標準データベース監査機能を使用して、接続および切断のみでなく、セッション・アクティビティ(物理I/O、論理I/O、デッドロックなど)も記録できます。 

トリガーを使用して高度な監査を行うには、通常、AFTERトリガーを使用します。AFTERトリガーを使用すると、トリガーを実行する文が適切な整合性制約に従った後で、監査情報が記録されます。これによって、整合性制約の例外を生成する文に対する無効な監査処理の実行を防止します。

AFTER行トリガーとAFTER文トリガーの使い分けは、監査情報に応じて異なります。たとえば、行トリガーを使用すると、表の行単位の値に基づく監査が可能です。トリガーでは、監査済SQL文を発行するための理由コードの入力をユーザーに要求することもできます。これは、行レベルおよび文レベルの両方の監査状況に有効です。

次の例では、Emp_tab表に対する変更を行ベースで監査するトリガーを示します。この例では、更新前に理由コードをグローバル・パッケージ変数に格納する必要があります。トリガーを使用して値ベースの監査を実行する方法、およびパブリック・パッケージ変数を使用する方法を示します。


注意

次のデータ構造を設定しないと機能しない例もあります。

CREATE OR REPLACE PACKAGE Auditpackage AS
Reason VARCHAR2(10);
PROCEDURE Set_reason(Reason VARCHAR2);
END;
CREATE TABLE Emp99 (
Empno NOT NULL NUMBER(4),
Ename VARCHAR2(10),
Job VARCHAR2(9),
Mgr NUMBER(4),
Hiredate DATE,
Sal NUMBER(7,2),
Comm NUMBER(7,2),
Deptno NUMBER(2),
Bonus NUMBER,
Ssn NUMBER,
Job_classification NUMBER);

CREATE TABLE Audit_employee (
Oldssn NUMBER,
Oldname VARCHAR2(10),
Oldjob VARCHAR2(2),
Oldsal NUMBER,
Newssn NUMBER,
Newname VARCHAR2(10),
Newjob VARCHAR2(2),
Newsal NUMBER,
Reason VARCHAR2(10),
User1 VARCHAR2(10),
Systemdate DATE);
 

CREATE OR REPLACE TRIGGER Audit_employee
AFTER INSERT OR DELETE OR UPDATE ON Emp99
FOR EACH ROW
BEGIN
/* AUDITPACKAGE is a package with a public package
   variable REASON.  REASON could be set by the
   application by a command such as EXECUTE
   AUDITPACKAGE.SET_REASON(reason_string). Note that a
   package variable has state for the duration of a
   session and that each session has a separate copy of
   all package variables. */

IF Auditpackage.Reason IS NULL THEN
   Raise_application_error(-20201, 'Must specify reason'
      || ' with AUDITPACKAGE.SET_REASON(Reason_string)');
END IF;

/* If the preceding conditional evaluates to TRUE, the
   user-specified error number and message is raised,
   the trigger stops execution, and the effects of the
   triggering statement are rolled back.  Otherwise, a
   new row is inserted into the predefined auditing
   table named AUDIT_EMPLOYEE containing the existing
   and new values of the Emp_tab table and the reason code
   defined by the REASON variable of AUDITPACKAGE.  Note
   that the "old" values are NULL if triggering
   statement is an INSERT and the "new" values are NULL
   if the triggering statement is a DELETE. */

INSERT INTO Audit_employee VALUES
   (:old.Ssn, :old.Ename, :old.Job_classification, :old.Sal,
   :new.Ssn, :new.Ename, :new.Job_classification, :new.Sal,
   auditpackage.Reason, User, Sysdate );
END;

更新のたびに強制的に理由コードを設定する場合は、理由コードをNULLに設定しなおすこともできます。次の簡単なAFTER文トリガーは、トリガーを実行する文が実行された後で理由コードをNULLに設定します。

CREATE OR REPLACE TRIGGER Audit_employee_reset
AFTER INSERT OR DELETE OR UPDATE ON Emp_tab
BEGIN
   auditpackage.set_reason(NULL);
END;

前述のトリガーは、2つとも同じ種類のSQL文によって起動されます。ただし、AFTER行トリガーが、トリガーを実行する文によって影響を受ける表の行ごとに1回起動されるのに対して、AFTER文トリガーは、トリガーを実行する文の実行が終了したときに1回のみ起動されます。

次に示すトリガーもトリガーを使用して監査を行います。このトリガーは、Emp_tab表に加えられた変更を追跡し、この情報をAUDIT_TABLEAUDIT_TABLE_VALUESに格納します。


注意

次のデータ構造を設定しないと機能しない例もあります。

CREATE TABLE Audit_table (
Seq NUMBER,
User_at VARCHAR2(10),
Time_now DATE,
Term VARCHAR2(10),
Job VARCHAR2(10),
Proc VARCHAR2(10),
enum NUMBER);
CREATE SEQUENCE Audit_seq;
CREATE TABLE Audit_table_values (
Seq NUMBER,
Dept NUMBER,
Dept1 NUMBER,
Dept2 NUMBER);
 

CREATE OR REPLACE TRIGGER Audit_emp
   AFTER INSERT OR UPDATE OR DELETE ON Emp_tab
   FOR EACH ROW
   DECLARE
      Time_now DATE;
      Terminal CHAR(10);
   BEGIN
      -- get current time, and the terminal of the user:
      Time_now := SYSDATE;
      Terminal := USERENV('TERMINAL');
      -- record new employee primary key
      IF INSERTING THEN 
         INSERT INTO Audit_table
            VALUES (Audit_seq.NEXTVAL, User, Time_now,
               Terminal, 'Emp_tab', 'INSERT', :new.Empno);
      -- record primary key of the deleted row:
      ELSIF DELETING THEN                           
         INSERT INTO Audit_table
            VALUES (Audit_seq.NEXTVAL, User, Time_now,
               Terminal, 'Emp_tab', 'DELETE', :old.Empno);
      -- for updates, record the primary key
      -- of the row being updated:
      ELSE 
         INSERT INTO Audit_table
            VALUES (audit_seq.NEXTVAL, User, Time_now,
               Terminal, 'Emp_tab', 'UPDATE', :old.Empno);
         -- and for SAL and DEPTNO, record old and new values:
         IF UPDATING ('SAL') THEN
            INSERT INTO Audit_table_values
               VALUES (Audit_seq.CURRVAL, 'SAL',
                  :old.Sal, :new.Sal);

         ELSIF UPDATING ('DEPTNO') THEN
            INSERT INTO Audit_table_values
               VALUES (Audit_seq.CURRVAL, 'DEPTNO',
                  :old.Deptno, :new.DEPTNO);
         END IF;
      END IF;
END;

整合性制約およびトリガー: 例

トリガーおよび宣言整合性制約は、両方ともデータ入力の制限に使用できます。ただし、トリガーと整合性制約には大きな違いがあります。

宣言整合性制約はデータベースに関する文で、これは常にTRUEです。表内の既存のデータおよび表を操作するすべての文に対して制約が適用されます。

関連項目

第6章「アプリケーション開発におけるデータ整合性のメンテナンス」 

トリガーは、トランザクションで可能な処理を制約します。トリガーは、トリガーが定義される前にロードされたデータには適用されません。このため、表内のすべてのデータが、対応付けられたトリガーによって確立されたルールに適合するかどうかは確認できません。

トリガーを使用してOracle Databaseの宣言整合性制約機能がサポートするルールと同じルールの多くを施行できますが、トリガーは、標準の整合性制約では定義できない複雑なビジネス・ルールを規定するためにのみ使用するようにしてください。Oracle Databaseの宣言整合性制約機能には、トリガーで定義する制約に比べて、次のメリットがあります。

一元化された整合性チェック:すべてのデータ・アクセス・ポイントは、各スキーマ・オブジェクトに対応する整合性制約によって定義されたグローバルな一連のルールに準拠する必要があります。

宣言方式:標準の整合性制約機能を使用して定義された制約は、トリガーで定義された同等の制約と比較して、より作成しやすくエラーが発生しにくいというメリットがあります。

データ整合性のほとんどは、宣言整合性制約によって定義して施行できますが、トリガーは宣言整合性制約では定義できない複雑なビジネス制約の施行に使用できます。たとえば、トリガーを使用して次を施行できます。

トリガーを使用した参照整合性

トリガーを使用して参照整合性を施行できる場合が多々あります。ただし、トリガーを使用するのは、実行しているアクションに宣言整合性がサポートされていない場合のみにしてください。

トリガーを使用して参照整合性をメンテナンスするときは、親表に主キー(または一意キー)制約を宣言します。同じデータベース内の親表と子表間の参照整合性をトリガーでメンテナンスしている場合は、子表にも外部キーを宣言できますが、外部キーは使用禁止に設定してください。使用禁止にすることによって、対応する主キー制約が(CASCADEオプションを使用して、主キー制約を明示的に削除しないかぎり)削除されなくなります。

トリガーを使用して参照整合性をメンテナンスするには、次のようにします。

次の項では、参照整合性の規定に必要なトリガーの例を紹介します。これらの例ではEmp_tab表およびDept_tab表を使用します。

トリガーのいくつかには、行をロックする文(SELECT... FOR UPDATE)が含まれています。この操作は、行を処理するときの同時実行性のメンテナンスに必要です。

子表に対する外部キー・トリガー

次のトリガーでは、INSERT文またはUPDATE文が外部キーに影響する前に、対応する値が親キー内に確実に存在するようにします。次の例に含まれる変更表例外によって、このトリガーをUPDATE_SET_DEFAULTトリガーおよびUPDATE_CASCADEトリガーとともに使用できるようになります。このトリガーを単独で使用する場合は、この例外を削除できます。

CREATE OR REPLACE TRIGGER Emp_dept_check
BEFORE INSERT OR UPDATE OF Deptno ON Emp_tab
FOR EACH ROW WHEN (new.Deptno IS NOT NULL)

-- Before a row is inserted, or DEPTNO is updated in the Emp_tab
-- table, fire this trigger to verify that the new foreign
-- key value (DEPTNO) is present in the Dept_tab table.
DECLARE
   Dummy              INTEGER;  -- to be used for cursor fetch
   Invalid_department EXCEPTION;
   Valid_department   EXCEPTION;
   Mutating_table     EXCEPTION;
   PRAGMA EXCEPTION_INIT (Mutating_table, -4091);

-- Cursor used to verify parent key value exists.  If
-- present, lock parent key's row so it can't be
-- deleted by another transaction until this
-- transaction is committed or rolled back.
  CURSOR Dummy_cursor (Dn NUMBER) IS
   SELECT Deptno FROM Dept_tab
      WHERE Deptno = Dn
         FOR UPDATE OF Deptno;
BEGIN
   OPEN Dummy_cursor (:new.Deptno);
   FETCH Dummy_cursor INTO Dummy;

   -- Verify parent key.  If not found, raise user-specified
   -- error number and message.  If found, close cursor
   -- before allowing triggering statement to complete:
   IF Dummy_cursor%NOTFOUND THEN
      RAISE Invalid_department;
   ELSE
      RAISE valid_department;
   END IF;
   CLOSE Dummy_cursor;
EXCEPTION
   WHEN Invalid_department THEN
      CLOSE Dummy_cursor;
      Raise_application_error(-20000, 'Invalid Department'
         || ' Number' || TO_CHAR(:new.deptno));
   WHEN Valid_department THEN
      CLOSE Dummy_cursor;
   WHEN Mutating_table THEN
      NULL;
END;
親表に対するUPDATEおよびDELETE RESTRICTトリガー

次のトリガーをDEPT_TAB表に定義し、DEPT_TAB表の主キーに対してUPDATEおよびDELETE RESTRICT参照アクションを施行します。

CREATE OR REPLACE TRIGGER Dept_restrict
BEFORE DELETE OR UPDATE OF Deptno ON Dept_tab
FOR EACH ROW

-- Before a row is deleted from Dept_tab or the primary key
-- (DEPTNO) of Dept_tab is updated, check for dependent
-- foreign key values in Emp_tab; rollback if any are found.
DECLARE
   Dummy                 INTEGER;      -- to be used for cursor fetch
   Employees_present     EXCEPTION;
   employees_not_present EXCEPTION;

   -- Cursor used to check for dependent foreign key values.
   CURSOR Dummy_cursor (Dn NUMBER) IS
      SELECT Deptno FROM Emp_tab WHERE Deptno = Dn;

BEGIN
   OPEN Dummy_cursor (:old.Deptno);
   FETCH Dummy_cursor INTO Dummy;
   -- If dependent foreign key is found, raise user-specified
   -- error number and message.  If not found, close cursor
   -- before allowing triggering statement to complete.
   IF Dummy_cursor%FOUND THEN
      RAISE Employees_present;     -- dependent rows exist 
   ELSE
      RAISE Employees_not_present; -- no dependent rows 
   END IF;
   CLOSE Dummy_cursor;

EXCEPTION
   WHEN Employees_present THEN
      CLOSE Dummy_cursor;
      Raise_application_error(-20001, 'Employees Present in'
         || ' Department ' || TO_CHAR(:old.DEPTNO));
   WHEN Employees_not_present THEN
      CLOSE Dummy_cursor;
END;


注意

このトリガーは、自己参照型の表(主キーまたは一意キーが存在し、さらに外部キーが存在する表)では機能しません。また、このトリガーでは、トリガーの循環(AがBを起動し、BがAを起動する)は使用できません。 


親表に対するUPDATEおよびDELETE SET NULLトリガー: 例

次のトリガーをDEPT_TAB表に定義し、DEPT_TAB表の主キーに対してUPDATEおよびDELETE SET NULL参照アクションを施行します。

CREATE OR REPLACE TRIGGER Dept_set_null
AFTER DELETE OR UPDATE OF Deptno ON Dept_tab
FOR EACH ROW

-- Before a row is deleted from Dept_tab or the primary key
-- (DEPTNO) of Dept_tab is updated, set all corresponding
-- dependent foreign key values in Emp_tab to NULL:
BEGIN
   IF UPDATING AND :OLD.Deptno != :NEW.Deptno OR DELETING THEN
      UPDATE Emp_tab SET Emp_tab.Deptno = NULL
         WHERE Emp_tab.Deptno = :old.Deptno;
   END IF;
END;
親表に対するDELETE CASCADEトリガー: 例

DEPT_TAB表に対する次のトリガーは、DEPT_TAB表の主キーに対してDELETE CASCADE参照アクションを施行します。

CREATE OR REPLACE TRIGGER Dept_del_cascade
AFTER DELETE ON Dept_tab
FOR EACH ROW

-- Before a row is deleted from Dept_tab, delete all
-- rows from the Emp_tab table whose DEPTNO is the same as
-- the DEPTNO being deleted from the Dept_tab table:
BEGIN
   DELETE FROM Emp_tab
      WHERE Emp_tab.Deptno = :old.Deptno;
END;


注意

通常、DELETE CASCADEのコードは、更新および削除の両方の可能性を考慮して、UPDATE SET NULLまたはUPDATE SET DEFAULTのコードと組み合されます。 


親表に対するUPDATE CASCADEトリガー: 例

次のトリガーは、Dept_tab表の部門番号が更新されたときに、その変更がEmp_tab表の依存外部キーに確実に伝播されるようにします。

-- Generate a sequence number to be used as a flag for
-- determining if an update has occurred on a column:
CREATE SEQUENCE Update_sequence
    INCREMENT BY 1 MAXVALUE 5000
    CYCLE;

CREATE OR REPLACE PACKAGE Integritypackage AS
   Updateseq NUMBER;
END Integritypackage;

CREATE OR REPLACE PACKAGE BODY Integritypackage AS
END Integritypackage;
-- create flag col:
ALTER TABLE Emp_tab ADD Update_id NUMBER;

CREATE OR REPLACE TRIGGER Dept_cascade1 BEFORE UPDATE OF Deptno ON Dept_tab
DECLARE
   Dummy NUMBER;

-- Before updating the Dept_tab table (this is a statement
-- trigger), generate a new sequence number and assign
-- it to the public variable UPDATESEQ of a user-defined
-- package named INTEGRITYPACKAGE:
BEGIN
   SELECT Update_sequence.NEXTVAL
      INTO Dummy
      FROM dual;
   Integritypackage.Updateseq := Dummy;
END;

CREATE OR REPLACE TRIGGER Dept_cascade2 AFTER DELETE OR UPDATE
   OF Deptno ON Dept_tab FOR EACH ROW

-- For each department number in Dept_tab that is updated,
-- cascade the update to dependent foreign keys in the
-- Emp_tab table.  Only cascade the update if the child row
-- has not already been updated by this trigger:
BEGIN
   IF UPDATING THEN
      UPDATE Emp_tab
         SET Deptno = :new.Deptno,
         Update_id = Integritypackage.Updateseq   --from 1st
         WHERE Emp_tab.Deptno = :old.Deptno
         AND Update_id IS NULL;
         /* only NULL if not updated by the 3rd trigger
            fired by this same triggering statement */
   END IF;
   IF DELETING THEN

   -- Before a row is deleted from Dept_tab, delete all
   -- rows from the Emp_tab table whose DEPTNO is the same as
   -- the DEPTNO being deleted from the Dept_tab table:
      DELETE FROM Emp_tab
      WHERE Emp_tab.Deptno = :old.Deptno;
   END IF;
END;
CREATE OR REPLACE TRIGGER Dept_cascade3 AFTER UPDATE OF Deptno ON Dept_tab
BEGIN  UPDATE Emp_tab
   SET Update_id = NULL
   WHERE Update_id = Integritypackage.Updateseq;
END;


注意

このトリガーによってEmp_tab表が更新されるため、Emp_dept_checkトリガーも起動されます(トリガーが使用可能になっている場合)。結果の変更表エラーは、Emp_dept_checkトリガーによって検出されます。エラーの検出を必要とするトリガーは、本番環境で常に正常に動作することを保証するために、慎重にテストする必要があります。 


複雑なCHECK制約に対するトリガー: 例

トリガーは、参照整合性以外の整合性ルールも施行できます。たとえば、次のトリガーは、トリガーを実行する文の実行を許可する前に、複雑なチェックを実行します。


注意

次のデータ構造を設定しないと機能しない例もあります。

CREATE TABLE Salgrade (
Grade NUMBER,
Losal NUMBER,
Hisal NUMBER,
Job_classification NUMBER)
 

CREATE OR REPLACE TRIGGER Salary_check
BEFORE INSERT OR UPDATE OF Sal, Job ON Emp99
FOR EACH ROW
DECLARE
   Minsal                NUMBER;
   Maxsal                NUMBER;
   Salary_out_of_range   EXCEPTION;
BEGIN

/* Retrieve the minimum and maximum salary for the
   employee's new job classification from the SALGRADE
   table into MINSAL and MAXSAL: */

SELECT Minsal, Maxsal INTO Minsal, Maxsal FROM Salgrade
   WHERE Job_classification = :new.Job;


/* If the employee's new salary is less than or greater
   than the job classification's limits, the exception is
   raised.  The exception message is returned and the
   pending INSERT or UPDATE statement that fired the
   trigger is rolled back:*/

   IF (:new.Sal < Minsal OR :new.Sal > Maxsal) THEN
      RAISE Salary_out_of_range;
   END IF;
EXCEPTION
   WHEN Salary_out_of_range THEN
      Raise_application_error (-20300,
         'Salary '||TO_CHAR(:new.Sal)||' out of range for '
         ||'job classification '||:new.Job
         ||' for employee '||:new.Ename);
   WHEN NO_DATA_FOUND THEN
      Raise_application_error(-20322,
         'Invalid Job Classification '
         ||:new.Job_classification);
END;
複雑なセキュリティ認可およびトリガー: 例

トリガーは、一般に、表データに対する複雑なセキュリティ認可の施行によく使用されます。トリガーは、Oracle Databaseが提供するデータベース・セキュリティ機能では定義できない複雑なセキュリティ認可の施行にのみ使用します。たとえば、トリガーを使用して、週末、休日および休業日には、Emp_tab表の給与データを更新できないようにすることができます。

複雑なセキュリティ認可の施行にトリガーを使用する場合は、BEFORE文トリガーを使用すると最も効果的です。BEFORE文トリガーを使用すると、次のようなメリットがあります。

次の例は、セキュリティを施行するために使用するトリガーを示します。


注意

次のデータ構造を設定しないと機能しない例もあります。

CREATE TABLE Company_holidays (Day DATE);
 

CREATE OR REPLACE TRIGGER Emp_permit_changes
BEFORE INSERT OR DELETE OR UPDATE ON Emp99
DECLARE
   Dummy             INTEGER;
   Not_on_weekends   EXCEPTION;
   Not_on_holidays   EXCEPTION;
   Non_working_hours EXCEPTION;
BEGIN
   /* check for weekends: */
   IF (TO_CHAR(Sysdate, 'DY') = 'SAT' OR
      TO_CHAR(Sysdate, 'DY') = 'SUN') THEN
      RAISE Not_on_weekends;
   END IF;
   /* check for company holidays:*/
   SELECT COUNT(*) INTO Dummy FROM Company_holidays
      WHERE TRUNC(Day) = TRUNC(Sysdate);
      /* TRUNC gets rid of time parts of dates: */
   IF dummy > 0 THEN
      RAISE Not_on_holidays;
   END IF;
   /* Check for work hours (8am to 6pm): */
   IF (TO_CHAR(Sysdate, 'HH24') < 8 OR
       TO_CHAR(Sysdate, 'HH24') > 18) THEN
       RAISE Non_working_hours;
   END IF;
EXCEPTION
   WHEN Not_on_weekends THEN
      Raise_application_error(-20324,'May not change '
         ||'employee table during the weekend');
   WHEN Not_on_holidays THEN
      Raise_application_error(-20325,'May not change '
         ||'employee table during a holiday');
   WHEN Non_working_hours THEN
      Raise_application_error(-20326,'May not change '
      ||'Emp_tab table during non-working hours');
END;

関連項目

データベース・セキュリティ機能の詳細は、『Oracle Databaseセキュリティ・ガイド』を参照してください。 

透過的なイベント・ロギングおよびトリガー

特定のイベントに続いてデータベースに透過的な変更を実行する場合、トリガーは非常に効果的です。

REORDERトリガーの例では、一定の条件が満たされると、必要に応じて部品を再注文するトリガーを示します。(トリガーを実行する文が入力され、PARTS_ON_HAND値がREORDER_POINT値より小さい場合です。)

導出列値およびトリガー: 例

トリガーは、INSERT文またはUPDATE文に指定される値に基づいて、列の値を自動的に取得できます。この種のトリガーは、同一行上の別の列の値に依存する特定の列に値を埋め込む場合に便利です。この種の操作を実行するには、BEFORE行トリガーが必要ですが、これは次の理由によります。

次の例では、行が挿入または更新されるたびに、表の新しい列値を導出するトリガーの使用方法を示します。


注意

次のデータ構造を設定しないと機能しない例もあります。

ALTER TABLE Emp99 ADD(
Uppername VARCHAR2(20),
Soundexname VARCHAR2(20));
 

CREATE OR REPLACE TRIGGER Derived 
BEFORE INSERT OR UPDATE OF Ename ON Emp99

/* Before updating the ENAME field, derive the values for
   the UPPERNAME and SOUNDEXNAME fields. Users should be
   restricted from updating these fields directly: */
FOR EACH ROW
BEGIN
   :new.Uppername := UPPER(:new.Ename);
   :new.Soundexname := SOUNDEX(:new.Ename);
END;
トリガーを使用した複合更新可能ビューの作成: 例

ビューは、表データに対して論理ウィンドウを提供する優れた手段です。ただし、ビュー問合せが複雑になると、ビューに対するDMLから基礎となる表に対するDMLへの変換を、システムが暗黙的には実行できなくなります。この問題の解決には、INSTEAD OFトリガーが有効です。このトリガーは、ビューに対して定義することができ、実際のDMLのかわりに起動されます。

書籍が書名の順に配置されているライブラリ・システムを考えてみます。このライブラリは、書籍型オブジェクトのコレクションで構成されています。次の例はこのスキーマについて説明したものです。

CREATE OR REPLACE TYPE Book_t AS OBJECT
(
   Booknum   NUMBER,
   Title     VARCHAR2(20),
   Author    VARCHAR2(20),
   Available CHAR(1)
);
CREATE OR REPLACE TYPE Book_list_t AS TABLE OF Book_t;

関係スキーマに次の表があるとします。

Table Book_table (Booknum, Section, Title, Author, Available)

Booknum  参照先  Title  Author  Available 

121001 

Classic 

Iliad 

Homer 

121002  

Novel 

Gone With the Wind 

Mitchell M 

このライブラリは、library_table(section)で構成されています。

参照先 

Geography 

Classic 

これらの表に対して複合ビューを定義し、セクションおよび各セクション内の書籍集合を示すライブラリの論理ビューを作成することができます。

CREATE OR REPLACE VIEW Library_view AS
SELECT i.Section, CAST (MULTISET (
   SELECT b.Booknum, b.Title, b.Author, b.Available
   FROM Book_table b
   WHERE b.Section = i.Section) AS Book_list_t) BOOKLIST
FROM Library_table i;

このビューに対してINSTEAD OFトリガーを定義し、このビューを更新可能にします。

CREATE OR REPLACE TRIGGER Library_trigger INSTEAD OF INSERT ON Library_view FOR EACH 
ROW
   Bookvar BOOK_T;
   i       INTEGER;
BEGIN
   INSERT INTO Library_table VALUES (:NEW.Section);
   FOR i IN 1..:NEW.Booklist.COUNT LOOP
      Bookvar := Booklist(i);
      INSERT INTO book_table
         VALUES ( Bookvar.booknum, :NEW.Section, Bookvar.Title, Bookvar.Author, 
bookvar.Available);
   END LOOP;
END;
/

これでlibrary_viewは更新可能ビューになり、このビューに対するすべてのINSERTは、自動的に起動されるトリガーによって処理されます。次に例を示します。

INSERT INTO Library_view VALUES ('History', book_list_t(book_t(121330, 'Alexander', 
'Mirth', 'Y');

同様に、ネストした表であるbooklistに対してトリガーを定義して、ネストした表の要素の変更を処理することもできます。

トリガーを使用したシステム・イベントの追跡
トリガーを使用したファイングレイン・アクセス・コントロール: 例

システム・トリガーは、アプリケーション・コンテキストの設定に使用できます。アプリケーション・コンテキストは比較的新しい機能であり、ファイングレイン・アクセス・コントロールを実装する機能が向上します。アプリケーション・コンテキストは保護されたセッション・キャッシュで、セッション固有の属性の格納に使用できます。

次に示す例では、set_ctxプロシージャがユーザー・プロファイルに基づいてアプリケーション・コンテキストを設定します。setexpensectxトリガーは、コンテキストがユーザーごとに確実に設定されるようにします。

CONNECT secdemo/secdemo

CREATE OR REPLACE CONTEXT Expenses_reporting USING Secdemo.Exprep_ctx;

REM =================================================================
REM Creation of the package which implements the context:
REM =================================================================

CREATE OR REPLACE PACKAGE Exprep_ctx AS
 PROCEDURE Set_ctx;
END;

SHOW ERRORS

CREATE OR REPLACE PACKAGE BODY Exprep_ctx IS
   PROCEDURE Set_ctx IS
      Empnum   NUMBER;
      Countrec NUMBER;
      Cc       NUMBER;
      Role     VARCHAR2(20);
   BEGIN

      -- SET emp_number:
      SELECT Employee_id INTO Empnum FROM Employee
         WHERE Last_name = SYS_CONTEXT('userenv', 'session_user');

      DBMS_SESSION.SET_CONTEXT('expenses_reporting','emp_number', Empnum);

      -- SET ROLE:
      SELECT COUNT (*) INTO Countrec FROM Cost_center WHERE Manager_id=Empnum;
      IF (countrec > 0) THEN
         DBMS_SESSION.SET_CONTEXT('expenses_reporting','exp_role','MANAGER');
      ELSE
         DBMS_SESSION.SET_CONTEXT('expenses_reporting','exp_role','EMPLOYEE');
      END IF;

      -- SET cc_number:
      SELECT Cost_center_id INTO Cc FROM Employee
         WHERE Last_name = SYS_CONTEXT('userenv','session_user');
      DBMS_SESSION.SET_CONTEXT(expenses_reporting','cc_number',Cc);
   END;
END;
CALL文の構文

CREATE OR REPLACE TRIGGER Secdemo.Setexpseetx
AFTER LOGON ON DATABASE
CALL Secdemo.Exprep_etx.Set_otx

トリガーを介したシステム・イベントに対する応答

システム・イベントを発行することによって、アプリケーションは、他のアプリケーションからのメッセージにサブスクライブするのと同じ方法で、データベース・イベントにサブスクライブできます。システム・イベント発行フレームワークには、次の機能が含まれています。

トリガーを作成すると、イベント発生時に実行するプロシージャを指定できます。DMLイベントは表でサポートされ、システム・イベントは、データベースおよびスキーマでサポートされます。ALTER TRIGGER文を使用してトリガーを使用可能および使用禁止にすると、通知をオンおよびオフにすることができます。

この機能は、アドバンスト・キューイング・エンジンに統合されています。パブリッシュ/サブスクライブ・アプリケーションは、DBMS_AQ.ENQUEUE()プロシージャを使用し、他のアプリケーション(カートリッジなど)は、コールアウトを使用します。

関連項目

  • 『Oracle Database SQLリファレンス』

  • 発行済のイベントへのサブスクライブ方法の詳細は、『Oracle Streamsアドバンスト・キューイング・ユーザーズ・ガイドおよびリファレンス』を参照してください。

 

トリガーを介したイベントの発行方法

データベースによってイベントが検出されると、トリガー・メカニズムがトリガー内に指定されているアクションを実行します。このアクションの一部として、DBMS_AQパッケージを使用してイベントをキューに発行でき、キューによって、サブスクライバが通知を取得します。


注意

この方法で検出できるのは、システム定義のデータベース・イベントのみです。独自のイベント条件は定義できません。  


イベントが発生すると、そのイベントに対して使用可能なすべてのトリガーがデータベース上で起動されます。これには次の例外があります。

オブジェクトに対して複数のトリガーを作成できます。イベントが複数のトリガーを起動する場合、起動順序は定義されないため、特定の順序内で起動されるトリガーに依存しないでください。

発行コンテキスト

イベントが発行されるときは、パラメータ・リストに指定されている特定の実行時コンテキストおよび属性がコールアウト・プロシージャに渡されます。イベント属性関数と呼ばれる一連の関数が提供されています。

関連項目

イベント固有の属性の詳細は、「イベント属性関数」を参照してください。 

サポートされているシステム・イベントごとに、イベント固有の属性が指定され、事前定義されています。パラメータ・リストには、他の単純な式とともにこの属性のいずれかを選択できます。コールアウトの場合、これらはIN引数として渡されます。

エラー処理

発行コールアウト関数からの戻り状態は、すべてのイベントに関して無視されます。たとえば、SHUTDOWNイベントの場合、データベースは戻り状態に関しては何も実行できません。

関連項目

戻り状態の詳細は、「データベース・イベントのリスト」を参照してください。 

実行モデル

トリガーは、従来からトリガーの定義者として実行されてきました。イベントのトリガー・アクションは、アクションの定義者として(コールアウト内のパッケージまたはファンクションの定義者、またはキュー内のトリガーの所有者として)実行されます。トリガーの所有者には、基になるキュー、パッケージまたはプロシージャに対するEXECUTE権限が必要なため、この動作には一貫性があります。

イベント属性関数

データベースでトリガーが起動されると、トリガーを起動したイベントの属性を検索できます。ファンクション・コールを使用して各属性を検索できます。表9-2では、システムで定義されたイベント属性について説明します。


注意

  • これらの属性を使用可能にするには、まず、CATPROC.SQLスクリプトを実行する必要があります。

  • トリガー・ディクショナリ・オブジェクトは、発行されるイベントに関するメタデータおよびそれに対応する属性をメンテナンスします。

  • 以前のリリースでは、これらのファンクションはSYSパッケージを介してアクセスされていました。名前がora_で始まるこれらのパブリック・シノニムを使用することをお薦めします。

 

表 9-2    システムで定義されたイベント属性 
属性    説明   
ora_client_ip_address
 
VARCHAR2
 

基礎となるプロトコルがTCP/IPのとき、LOGONイベントでクライアントのIPアドレスを戻します。 

DECLARE
v_addr VARCHAR2(11);
IF (ora_sysevent = 'LOGON') THEN
v_addr := ora_client_ip_address;
END IF;
END;
 
ora_database_name
 
VARCHAR2(50)
 

データベース名を戻します。 

DECLARE
v_db_name VARCHAR2(50);
BEGIN
v_db_name := ora_database_name;
END;
 
ora_des_encrypted_password
 
VARCHAR2
 

作成または変更されるユーザーのDES暗号化パスワードを戻します。 

IF (ora_dict_obj_type = 'USER') THEN  
INSERT INTO event_table
VALUES (ora_des_encrypted_password);
END IF;
 
ora_dict_obj_name
 
VARCHAR(30)
 

DDL操作が発生したディクショナリ・オブジェクトの名前を戻します。 

INSERT INTO event_table 
VALUES ('Changed object is ' ||
ora_dict_obj_name);
 
ora_dict_obj_name_list
(name_list OUT ora_name_list_t)
 
BINARY_INTEGER
 

このイベントで変更されるオブジェクトのオブジェクト名のリストを戻します。 

IF (ora_sysevent='ASSOCIATE STATISTICS')
THEN number_modified :=
ora_dict_obj_name_list(name_list);
END IF;
 
ora_dict_obj_owner
 
VARCHAR(30)
 

DDL操作が発生したディクショナリ・オブジェクトの所有者を戻します。 

INSERT INTO event_table 
VALUES ('object owner is' ||
ora_dict_obj_owner);
 
ora_dict_obj_owner_list
(owner_list OUT ora_name_list_t)
 
BINARY_INTEGER
 

このイベントで変更されるオブジェクトのオブジェクト所有者のリストを戻します。 

IF (ora_sysevent='ASSOCIATE STATISTICS') 
THEN number_of_modified_objects :=
ora_dict_obj_owner_list(owner_list);
END IF;
 
ora_dict_obj_type
 
VARCHAR(20)
 

DDL操作が発生したディクショナリ・オブジェクトのタイプを戻します。 

INSERT INTO event_table 
VALUES ('This object is a ' ||
ora_dict_obj_type);
 
ora_grantee
(user_list OUT ora_name_list_t)
 
BINARY_INTEGER
 

OUTパラメータで権限付与イベントの権限受領者を戻し、戻り値で権限受領者の数を戻します。 

IF (ora_sysevent = 'GRANT') THEN
number_of_users=ora_grantee(user_list);
END IF;
 
ora_instance_num
 
NUMBER
 

インスタンス番号を戻します。 

IF (ora_instance_num = 1) THEN
INSERT INTO event_table VALUES ('1');
END IF;
 
ora_is_alter_column
(column_name IN VARCHAR2)
 
BOOLEAN
 

指定された列が変更された場合、trueを戻します。 

IF (ora_sysevent = 'ALTER' AND
ora_dict_obj_type = 'TABLE') THEN
alter_column := ora_is_alter_column('C');
END IF;
 
ora_is_creating_nested_table
 
BOOLEAN
 

現行のイベントがネストした表を作成しているときに、trueを戻します。 

IF (ora_sysevent = 'CREATE' and
ora_dict_obj_type = 'TABLE' and
ora_is_creating_nested_table) THEN
INSERT INTO event_table
VALUES ('A nested table is created');
END IF;
 
ora_is_drop_column
(column_name IN VARCHAR2)
 
BOOLEAN
 

指定された列が削除された場合、trueを戻します。 

IF (ora_sysevent = 'ALTER' AND
ora_dict_obj_type = 'TABLE') THEN
drop_column := ora_is_drop_column('C');
END IF;
 
ora_is_servererror
 
BOOLEAN
 

指定されたエラーがエラー・スタック上にある場合はtrue、ない場合はfalseを戻します。 

IF (ora_is_servererror(error_number)) THEN
INSERT INTO event_table
VALUES ('Server error!!');
END IF;
 
ora_login_user
 
VARCHAR2(30)
 

ログイン・ユーザー名を戻します。 

SELECT ora_login_user 
FROM dual;
 
ora_partition_pos
 
BINARY_INTEGER
 

CREATE TABLEINSTEAD OFトリガーでは、PARTITION句を挿入できるSQLテキスト内の位置を戻します。 

-- Retrieve ora_sql_txt into
-- sql_text variable first.
v_n := ora_partition_pos;
v_new_stmt := SUBSTR(sql_text,1,v_n - 1)
|| ' ' || my_partition_clause
|| ' ' || SUBSTR(sql_text, v_n));
 
ora_privilege_list
(privilege_list
OUT ora_name_list_t)
 
BINARY_INTEGER
 

権限受領者によって付与された権限のリストまたは取消し側から取り消された権限のリストを、OUTパラメータで戻します。戻り値として権限の数を戻します。 

IF (ora_sysevent = 'GRANT' OR
ora_sysevent = 'REVOKE') THEN
number_of_privileges :=
ora_privilege_list(priv_list);
END IF;
 
ora_revokee
(user_list OUT ora_name_list_t)
 
BINARY_INTEGER
 

OUTパラメータで取消しイベントの取消し側を戻します。戻り値として取消し側の数を戻します。 

IF (ora_sysevent = 'REVOKE') THEN
number_of_users := ora_revokee(user_list);
 
ora_server_error
 
NUMBER
 

(スタックの一番上を1として)位置を指定すると、エラー・スタック上のその位置のエラー番号を戻します。 

INSERT INTO event_table 
VALUES ('top stack error ' ||
ora_server_error(1));

 
ora_server_error_depth
 
BINARY_INTEGER
 

エラー・スタック上のエラー・メッセージの合計数を戻します。  

n := ora_server_error_depth;
-- This value is used with other functions -- such as ora_server_error
 
ora_server_error_msg
(position in binary_integer)
 
VARCHAR2
 

(スタックの一番上を1として)位置を指定すると、エラー・スタック上のその位置のエラー・メッセージを戻します。 

INSERT INTO event_table
VALUES ('top stack error message' ||
ora_server_error_msg(1));

 
ora_server_error_num_params
(position in binary_integer)
 
BINARY_INTEGER
 

(スタックの一番上を1として)位置を指定すると、「%s」などの書式を使用してエラー・メッセージ内で置き換えられた文字列数を戻します。 

n := ora_server_error_num_params(1);

 
ora_server_error_param
(position in binary_integer,
param in binary_integer)
 
VARCHAR2
 

(スタックの一番上を1として)位置およびパラメータ番号を指定すると、「%s」「%d」などに一致するエラー・メッセージ内の代入値を戻します。 

-- For example, the second %s in a 
-- message: "Expected %s, found %s"
param := ora_server_error_param(1,2);

 
ora_sql_txt
(sql_text out ora_name_list_t)
 
BINARY_INTEGER
 

OUTパラメータ内のトリガーを実行する文のSQLテキストを戻します。この文が長い場合、複数のPL/SQL表要素に分割されます。ファンクションの戻り値は、PL/SQL表内の要素数を示します。 

sql_text ora_name_list_t;
v_stmt VARCHAR2(2000);
...
n := ora_sql_txt(sql_text);
FOR i IN 1..n LOOP
v_stmt := v_stmt || sql_text(i);
END LOOP;
INSERT INTO event_table
VALUES ('text of triggering statement: '
|| v_stmt);
 
ora_sysevent
 
VARCHAR2(20)
 

トリガーを起動するシステム・イベントを戻します。イベント名は、構文にある名前と同じです。 

INSERT INTO event_table
VALUES (ora_sysevent);
 
ora_with_grant_option
 
BOOLEAN
 

権限オプションとともに権限が付与された場合、trueを戻します。 

IF (ora_sysevent = 'GRANT' and
ora_with_grant_option = TRUE) THEN
INSERT INTO event_table
VALUES ('with grant option');
END IF;
 
space_error_info
(error_number OUT NUMBER,
error_type OUT VARCHAR2,
object_owner OUT VARCHAR2,
table_space_name OUT
VARCHAR2,
object_name OUT VARCHAR2,
sub_object_name OUT VARCHAR2)
 
BOOLEAN
 

エラーが領域不足状態に関連する場合trueを戻し、エラーの原因となったオブジェクトに関する情報をOUTパラメータで戻します。 

IF (space_error_info(eno,typ,owner,ts,obj, 
subobj) = TRUE) THEN
DBMS_OUTPUT.PUT_LINE('The object '|| obj
|| ' owned by ' || owner ||
' has run out of space.');
END IF;
 

データベース・イベントのリスト

この項では、重要なシステム・イベントおよびクライアント・イベントについて説明します。

システム・イベント

システム・イベントは、個々の表または行ではなく、インスタンスまたはスキーマ全体に関係します。起動イベントおよび停止イベントに対して作成されたトリガーは、データベース・インスタンスに関連付けられる必要があります。エラー・イベントおよび一時停止イベントに対して作成されたトリガーは、データベース・インスタンスまたは特定のスキーマのいずれかへの関連付けが可能です。

表9-3に、システム・マネージャ・イベントのリストを示します。

表 9-3    システム・マネージャ・イベント 
イベント  起動するタイミング  条件  制限事項  トランザクション  属性関数 
STARTUP
 

データベースのオープン時 

なし 

トリガーではデータベース処理はできません。

戻り状態は無視されます。 

トリガーの起動後、別のトランザクションを開始してこれをコミットします。 

ora_sysevent
ora_login_user
ora_instance_num
ora_database_name
 
SHUTDOWN
 

サーバーがインスタンス停止を開始する直前

これによって、カートリッジを完全に停止できます。インスタンスの異常停止の場合は、このイベントは起動されない場合があります。 

なし 

トリガーではデータベース処理はできません。

戻り状態は無視されます。 

トリガーの起動後、別のトランザクションを開始してこれをコミットします。 

ora_sysevent
ora_login_user
ora_instance_num
ora_database_name
 
DB_ROLE_CHANGE
 

ロール変更後、初めてのデータベースのオープン時 

なし 

戻り状態は無視されます。 

トリガーの起動後、別のトランザクションを開始してこれをコミットします。 

ora_sysevent
ora_login_user
ora_instance_num
ora_database_name
 
SERVERERROR
 

enoエラーの発生時条件が指定されない場合、このイベントは任意のエラーが発生したときに起動します。

ORA-1034ORA-1403ORA-1422ORA-1423およびORA-4030の場合、トリガーは起動されません。本当のエラーでないか、または問題が深刻で処理を続行できないためです。また、ORA-18およびORA-20の場合もトリガーは起動されません。プロセスで、データベースに接続してエラーを記録することができないためです。 

ERRNO = eno 

エラーに依存します。

戻り状態は無視されます。 

トリガーの起動後、別のトランザクションを開始してこれをコミットします。 

ora_sysevent
ora_login_user
ora_instance_num
ora_database_name
ora_server_error
ora_is_servererror
space_error_info
 

クライアント・イベント

クライアント・イベントは、ユーザーのログイン/ログオフ、DMLおよびDDLの操作に関係するイベントです。次に例を示します。

CREATE OR REPLACE TRIGGER On_Logon  
  AFTER LOGON  
  ON The_user.Schema  
BEGIN  
  Do_Something;  
END;  

LOGONイベントおよびLOGOFFイベントによって、UIDおよびUSERに単純な条件を使用できます。 他のすべてのイベントによって、UIDUSERのような関数のみでなく、オブジェクトの型および名前に単純な条件を使用できます。

LOGONイベントは、トリガーの起動後、別のトランザクションを開始してこれをコミットします。他のすべてのイベントは、既存のユーザー・トランザクションでトリガーを起動します。

LOGONイベントおよびLOGOFFイベントは、すべてのオブジェクトを操作できます。他のすべてのイベントでは、対応するトリガーは、そのイベントを生成させるオブジェクト上でDROPALTERなどのDDL操作を実行できません。

これらのトリガーで使用できるDDLは、表の変更、作成または削除、トリガーの作成および処理のコンパイルです。

イベント・トリガーがDDL操作(CREATE TRIGGERなど)の対象になる場合、このトリガーを、後で同じトランザクション中に起動することはできません。

表9-4に、クライアント・イベントのリストを示します。

表 9-4    クライアント・イベント 
イベント  起動するタイミング  属性関数 
BEFORE ALTER

AFTER ALTER
 

カタログ・オブジェクトの変更時 

ora_sysevent 
ora_login_user
ora_instance_num
ora_database_name
ora_dict_obj_type
ora_dict_obj_name
ora_dict_obj_owner
ora_des_encrypted_password
(for ALTER USER events)
ora_is_alter_column
(for ALTER TABLE events)
ora_is_drop_column
(for ALTER TABLE events)
 
BEFORE DROP

AFTER DROP
 

カタログ・オブジェクトの削除時  

ora_sysevent
ora_login_user
ora_instance_num
ora_database_name
ora_dict_obj_type
ora_dict_obj_name
ora_dict_obj_owner
 
BEFORE ANALYZE

AFTER ANALYZE
 

分析文の発行時 

ora_sysevent
ora_login_user
ora_instance_num
ora_database_name
ora_dict_obj_name
ora_dict_obj_type
ora_dict_obj_owner
 
BEFORE ASSOCIATE STATISTICS

AFTER ASSOCIATE STATISTICS
 

関連統計文の発行時 

ora_sysevent
ora_login_user
ora_instance_num
ora_database_name
ora_dict_obj_name
ora_dict_obj_type
ora_dict_obj_owner
ora_dict_obj_name_list
ora_dict_obj_owner_list
 
BEFORE AUDIT
AFTER AUDIT

BEFORE NOAUDIT
AFTER NOAUDIT
 

監査または非監査文の発行時 

ora_sysevent
ora_login_user
ora_instance_num
ora_database_name
 
BEFORE COMMENT

AFTER COMMENT
 

オブジェクトへのコメントの追加時 

ora_sysevent
ora_login_user
ora_instance_num
ora_database_name
ora_dict_obj_name
ora_dict_obj_type
ora_dict_obj_owner
 
BEFORE CREATE 

AFTER CREATE
 

カタログ・オブジェクトの作成時  

ora_sysevent 
ora_login_user
ora_instance_num
ora_database_name
ora_dict_obj_type
ora_dict_obj_name
ora_dict_obj_owner
ora_is_creating_nested_table
(for CREATE TABLE events)
 
BEFORE DDL

AFTER DDL
 

ほとんどのSQL DDL文の発行時。アドバンスト・キューの作成などのPL/SQLプロシージャ・インタフェースを介して発行されたALTER DATABASECREATE CONTROLFILECREATE DATABASEおよびDDLに対しては起動されません。 

ora_sysevent
ora_login_user
ora_instance_num
ora_database_name
ora_dict_obj_name
ora_dict_obj_type
ora_dict_obj_owner
 
BEFORE DISASSOCIATE STATISTICS

AFTER DISASSOCIATE STATISTICS
 

非関連統計文の発行時 

ora_sysevent
ora_login_user
ora_instance_num
ora_database_name
ora_dict_obj_name
ora_dict_obj_type
ora_dict_obj_owner
ora_dict_obj_name_list
ora_dict_obj_owner_list
 
BEFORE GRANT

AFTER GRANT
 

権限付与文の発行時 

ora_sysevent
ora_login_user
ora_instance_num
ora_database_name
ora_dict_obj_name
ora_dict_obj_type
ora_dict_obj_owner
ora_grantee
ora_with_grant_option
ora_privileges
 
BEFORE LOGOFF
 

ユーザー・ログオフの開始時 

ora_sysevent
ora_login_user
ora_instance_num
ora_database_name
 
AFTER LOGON
 

ユーザーが正常にログインした後 

ora_sysevent
ora_login_user
ora_instance_num
ora_database_name
ora_client_ip_address
 
BEFORE RENAME

AFTER RENAME
 

名前の変更文の発行時 

ora_sysevent
ora_login_user
ora_instance_num
ora_database_name
ora_dict_obj_name
ora_dict_obj_owner
ora_dict_obj_type
 
BEFORE REVOKE

AFTER REVOKE
 

取消し文の発行時 

ora_sysevent
ora_login_user
ora_instance_num
ora_database_name
ora_dict_obj_name
ora_dict_obj_type
ora_dict_obj_owner
ora_revokee
ora_privileges
 
AFTER SUSPEND
 

領域不足状態のためSQL文が一時停止された後。トリガーは、文が再開されるようにこの状態を修正する必要があります。 

ora_sysevent
ora_login_user
ora_instance_num
ora_database_name
ora_server_error
ora_is_servererror
space_error_info
 
BEFORE TRUNCATE

AFTER TRUNCATE
 

オブジェクトの切捨て時 

ora_sysevent
ora_login_user
ora_instance_num
ora_database_name
ora_dict_obj_name
ora_dict_obj_type
ora_dict_obj_owner
 

戻る 次へ
Oracle
Copyright © 2006 Oracle Corporation.

All Rights Reserved.
目次
目次
索引
索引