我是靠谱客的博主 忧伤鸭子,最近开发中收集的这篇文章主要介绍Oracle触发器(Trigger)-------------保证数据的正确性一、什么是触发器二、触发器的作用三、触发器类型四、触发器的语法五、触发器的使用,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

一、什么是触发器

前面已经介绍过存储过程,触发器和存储过程比较类似,它由PL/SQL编写并存储在数据库中,它可以调用存储过程,但触发器本身的调用和存储过程调用却是不一样的。存储过程由用户、应用程序、触发器或其他过程调用。但触发器只能由数据库的特定事件来触发,所谓的特定事件主要包括如下几种类型的事件。


1)用户在指定的表或视图中做DML操作,主要包括如下几种:
■ INSERT操作,在特定的表或视图中增加数据。
■ UPDATE操作,对特定的表或视图修改数据。
■ DELETE操作,删除特定表或视图的数据。


2)用户做DDL操作,主要包括如下几种:
■ CREATE操作,创建对象。
■ ALTER操作,修改对象。
■ DROP操作,删除对象。


3)数据库事件,主要包括如下几种:
■ LOGON/LOGOFF,用户的登录或注销。
■ STARTUP/SHUTDOWN,数据库的打开或关闭。
■ ERRORS,特定的错误消息等。
在以上事件中的一种或多种发生时就能使触发器运行。

 

二、触发器的作用

可以根据不同的事件进行触发器调用,它有着更加精细的控制能力,这种特性可以帮助开发人员完成很多普通PL/SQL语句完成不了的功能。下面介绍触发器的主要作用。
■自动生成自增长字段。例如,为了不让数据重复,一般需要设置一个主键,该主键的内容不需要用户来控制,由Oracle系统在后台控制,而且该主键的值必须唯一。在表中插入一条新的数据之前,需首先得到该主键的最大值,然后连同要插入的数据一起插入表中,从而避免数据的重复。
■ 执行更复杂的业务逻辑。普通的操作方式只能完成固定的数据变动,而使用触发器则在完成的基础功能上做额外的操作,以达到完成特殊业务的目的。
■ 防止无意义的数据操作。利用触发器可以把符合某些条件的数据加以限制,使其不能变动。
■ 提供审计。利用触发器可以跟踪对数据库的操作。也可以在指定的表或视图记录改变时,利用触发器把数据变动日志记录下来。
■ 允许或限制修改某些表。利用触发器可以限制表的变动。
■ 实现完整性规则。当一个表中的数据有变动时可以利用触发器修改这些变动数据在其他表中的关联数据(正常情况下可以利用外键进行限制)。
■ 保证数据的同步复制。
注意:建议开发人员只在必要时使用触发器,因为触发器可能造成比较复杂的相关依赖性,这种情况在大型的数据库中可能会带来麻烦。例如,某个触发器的触发很可能造成多个触发器的连锁触发,一旦这种连锁触发超过32个就会出现异常。

 

三、触发器类型

触发器可分为5种类型,具体内容如下:

■数据操纵语言(DML)触发器。此种类型的触发器定义到表上,当对表执行INSERT、UPDATE、DELETE操作时可以激发该类型的触发器。利用该类触发器可以复制、检查、替换某种符合指定条件的数据。按照触发级别可以分为两种方式,第一种为行级触发器,此种类型表示每条记录修改时都会激发该触发器;第二种为语句级触发器,此种类型表示当SQL语句执行时会激发该触发器,它和修改多少条记录没有关系。如果以数据的更改事件为准,则分为BEFORE和AFTER两种类型。
■ 数据定义语言(DDL)触发器。当定义CREATE、ALTER、DROP模式对象时会触发相关的触发器,在Oracle中可以简单地理解为一个用户就有一个和它同名的模式,利用它可以使得某些表不能被修改或删除。
■ 复合触发器。此种类型的触发器是Oracle 11g的新特性,它相当于在一个触发器中包含了4种类型的触发器,其中包含了BEFORE类型的语句级、BEFORE类型的行级、AFTER类型的语句级以及AFTER类型的行级触发器。这种把所有触发器都放到一个代码块中的做法使得变量的传递变得更加方便。
■ INSTEAD OF触发器。此种类型的触发器通常作用在视图上。对具有多个源表的视图做DML操作转换成对多个源表进行操作。
■ 用户和系统事件触发器。作用在数据库上由数据库事件激发的触发器,如登录和注销事件的触发器。利用它可以记录数据库的登录情况。
本小节简单介绍了触发器的类型,也有些机构把DML和DDL类型的触发器直接分为行触发器、语句触发器、BEFORE触发器、AFTER触发器。

 

四、触发器的语法

了解触发器的语法是使用它的第一步,它就像一个公式,能让我们迅速地创建出自己的触发器。

1)DML触发器的主要语法如下:

01  CREATE [ OR REPLACE ] TRIGGER [schema.] trigger
02  { BEFORE | AFTER | INSTEAD OF } 
03   { DELETE | INSERT | UPDATE
04       [ OF column [, column ]... ]
05   }
06    [ OR { DELETE | INSERT | UPDATE
07         [ OF column [, column]... ]
08         }
09    ]...
10  {ON [ schema. ]table | [ schema. ] view }
11    [ FOR EACH ROW ]
12    [ FOLLOWS [ schema. ] trigger [, [ schema. ] trigger ]... ]
13    [ ENABLE | DISABLE ]
14    [ WHEN (condition) ]
15    trigger_body

【语法说明】
■ OR REPLACE:新建的触发器可以覆盖原有的同名触发器。
■ TRIGGER:创建触发器的关键字。
■ schema:触发器所属模式(可简单看成用户名),如果不加该项则表示该触发器属于自己。
■ BEFORE:触发器类型为前触发。
■ AFTER:触发器类型为后触发。
■ INSTEAD OF:表示触发器类型为替换类型。
■ DELETE | INSERT | UPDATE:表示触发的事件。
■ [ OF column [, column ]:触发条件具体到的某列。
■ 第6~9行表示可以追加多个条件。
■ ON [ schema. ]table | [ schema. ] view:该触发器作用的表或视图,INSTEAD OF类型可以作用在视图上。
■ FOR EACH ROW:表示行级触发器,省略该关键字则为语句级触发器。
■ FOLLOWS [ schema. ] trigger:触发器的执行顺序。
■ ENABLE | DISABLE:设置触发器是否可用。
■ WHEN (condition):触发该触发器的条件。
■ trigger_body:表示触发器的函数体。

2)DDL和数据库事件触发器语法如下:

01  CREATE [ OR REPLACE ] TRIGGER [schema.] trigger
02    { BEFORE | AFTER }
03    { ddl_event [OR ddl_event]...
04    | database_event [OR database_event]...
05    }
06    ON { [schema.] SCHEMA
07       | DATABASE
08       }
09    [ FOLLOWS [ schema. ] trigger [, [ schema. ] trigger ]... ]
10    [ ENABLE | DISABLE ]
11    [ WHEN (condition) ]
12    trigger_body

【语法说明】
■ OR REPLACE:新建的触发器可以覆盖原有的同名触发器。
■ TRIGGER:创建触发器的关键字。
■ schema:触发器所属模式,如果不加该项则表示该触发器属于自己。
■ BEFORE:触发器类型为前触发。
■ AFTER:触发器类型为后触发。
■ ddl_event [OR ddl_event]:DDL事件,用OR连接。
■ database_event[OR database_event]:数据库事件,用OR连接。
■ [schema.] SCHEMA | DATABASE:触发器可作用在模式上或数据库上。
■ FOLLOWS [ schema. ] trigger:触发器的执行顺序。
■ ENABLE | DISABLE:设置触发器是否可用。
■ WHEN (condition):触发该触发器的条件。
■ trigger_body:表示触发器的函数体。

下表列出了一部分DDL事件,这些事件都可以激发触发器。

 

下表列出了部分数据库事件,这些事件也会激发触发器。

触发器由三部分组成,分别是触发事件或语句、触发器限制、触发器动作。
■ 触发事件或语句是指激发触发器的动作,具体指第11.1.1小节介绍的事件操作。
■ 触发器限制指的是WHEN后面的条件,当条件为TRUE时,该触发器会被激发。
■ 触发器动作就是一段过程,当触发器被激发时运行的trigger_body部分。

3)复合触发器也是DML触发器的一种,它的主要语法如下:

01  CREATE [ OR REPLACE ] TRIGGER [schema.] trigger
02  FOR 
03  { DELETE | INSERT | UPDATE
04       [ OF column [, column ]... ]
05  }
06    [ OR { DELETE | INSERT | UPDATE
07         [ OF column [, column]... ]
08         }
09    ]...
10  ON { [ schema. ]table
11     | [ schema. ] view
12     }
13  COMPOUND TRIGGER
14  { BEFORE STATEMENT IS tps_body END BEFORE STATEMENT]
15  | BEFORE EACH ROW  IS tps_body END BEFORE EACH ROW
16  | AFTER STATEMENT  IS tps_body END AFTER STATEMENT
17  | AFTER EACH ROW   IS tps_body END AFTER EACH ROW
18  }

【语法说明】
■ OR REPLACE:新建的触发器可以覆盖原有的同名触发器。
■ TRIGGER:创建触发器的关键字。
■ schema:触发器所属模式,如果不加该项则表示该触发器属于自己。
■ DELETE | INSERT | UPDATE:表示触发事件。
■ COMPOUND TRIGGER:定义触发器时表示为复合类型触发器。
■ BEFORE STATEMENT:前语句级触发。
■ BEFORE EACH ROW:前行级触发。
■ AFTER STATEMENT:后语句级触发。
■ AFTER EACH ROW:后行级触发。
该类型是Oracle 11g新增加的触发器类型,比较方便地处理事务的4个时间点,这将在后面章节介绍。

 

五、触发器的使用

 

1、老样子,示例中还是以PRODUCTINFO(产品信息表)和CATEGORYINFO(产品类型表)为基础,如下图:

【示例1】创建一个简单的触发器
这是一个入门的触发器示例,该触发器在执行删除操作时触发,作用在PRODUCTINFO表上。

01  CREATE TRIGGER FIRST_TGR
02       AFTER DELETE
03       ON PRODUCTINFO
04              BEGIN
05         IF DELETING THEN
06           DBMS_OUTPUT.put_line('删除数据操作!');
07         END IF;
08              END;
09   /

【代码解析】
■ 第1行表示创建名为FIRST_TGR的触发器。
■ 第2行表示触发器类型为后触发,触发事件是删除操作。
■ 第3行表示触发器作用的表是PRODUCTINFO。
■ 第5~7行表示如果对表PRODUCTINFO执行删除操作则输出第6行代码中的提示。

【验证触发器】
一旦触发器创建成功,那么对表PRODUCTINFO执行删除操作时就会激发该触发器,因为是语句级的触发器,所以激发该触发器的时机是在删除操作之后。如果触发,则输出脚本中第6行的提示。打开SQL*Plus的输出状态,执行如下删除操作:

DELETE FROM PRODUCTINFO WHERE PRODUCTID = '0240090001';

整个操作流程如下图(PL/SQL Developer中同样可行):

从图中会发现,删除操作中实际并没有数据被删除,但依然激发了触发器。也就是说,对于语句触发器是删除这个事件本身激发了触发器,与数据是否真的被删除没有必然联系。

2、  DML类型触发器

 

【示例2】创建行级触发器

要求当在PRODUCTINFO表中增加数据时将激发该触发器,并把所做的操作记录到表LOG_TAB中。该触发器属于单一事件的触发器。创建行级触发器分为以下3个步骤:

1)创建操作事件记录表。该表数据结构如下图所示。

语句如下:

01  CREATE TABLE LOG_TAB
02  (
03    ID             VARCHAR2(10) not null,
04    OPER_TABLE     VARCHAR2(20),
05    OPER_KD        VARCHAR2(10),
06    OPER_TABLE_PRK VARCHAR2(50),
07    OPER_DATE      DATE,
08    constraint LOG_TAB_PRK primary key (ID)
09  )
10  /

2)创建用做LOG_TAB表主键的自增长序列。

01  CREATE SEQUENCE LOG_TAB_ID
02  MINVALUE 1000000000
03  MAXVALUE 9999999999
04  START WITH 1000000000
05  INCREMENT BY 1
/

【代码解析】
■ 第1行表示创建名为LOG_TAB_ID的序列。
■ 第2行表示该序列的最小值为1 000 000 000。
■ 第3行表示该序列的最大值为9 999 999 999。
■ 第4行表示该序列的值从1 000 000 000开始。
■ 第5行表示该序列增长量为1。
表中主键字段的值不允许重复,在Oracle中不能直接设置某字段为自增长字段以达到符合主键值要求的目的,这种情况下的解决方法就是利用序列生成的值作为表主键的值。

3)创建触发器

01  CREATE TRIGGER PRODUCTINFO_OPER_TGR
02      BEFORE INSERT  
03      ON PRODUCTINFO
04      FOR EACH ROW 
05      BEGIN
06        IF INSERTING THEN
07                         INSERT INTO LOG_TAB
08                VALUES
09               (LOG_TAB_ID.NEXTVAL,
10                 'PRODUCTINFO',
11                 'INSERT',
12                   :NEW.PRODUCTID,
13                   SYSDATE);    
14                 DBMS_OUTPUT.put_line('插入数据主键是 ' || :new.PRODUCTID);
15             END IF;      
16      END;
17      /

【代码解析】
■ 第1行表示创建名为PRODUCTINFO_OPER_TGR的触发器。
■ 第2行表示该触发器为前触发,触发事件是INSERT操作。
■ 第3行表示该触发器作用在表PRODUCTINFO上。
■ 第4行表示该触发器为行级触发,也就是说每增加一行就会触发一次。
■ 第6行表示判断是否为插入数据操作,如果是则进入IF语句。
■ 第7~13行表示向事件记录表中增加数据。其中第9行的LOG_TAB_ID.NEXTVAL表示得到序列的下一个值,第13行的SYSDATE表示增加数据时的当前系统时间。
■ 第14行输出增加数据的PRODUCTID字段值,该字段是PRODUCTINFO表的主键。
在行级的触发器里使用:new或:old来访问变更前和变更后的数据。其中,如果是增加新记录操作,则只有:new可以访问;如果是修改操作,则:new和:old都可以访问;:new表示修改后的记录,:old表示修改前的记录;而删除则只有:old可以访问,因为该操作是删除已有的记录。

【验证触发器】
在表PRODUCTINFO中增加数据并查看是否正常激发触发器,脚本如下:

INSERT INTO PRODUCTINFO VALUES('0240090001','触发器测试',1000,0,0000000000,'','测试');

继续查询表PRODUCTINFO和表LOG_TAB,检查是否有对应数据增加。

以上是一个入门型的示例,该触发器中只包含了一种触发事件,就是增加数据的事件,但我们可以在一个触发器中添加多个触发事件,只需要把它们利用条件语句分开即可。下面一个示例演示了如何在一个触发器中使用多种触发事件。

【示例3】在触发器中使用多种触发事件

把示例2改为当增加、修改、删除记录时都能触发触发器,记录操作事件的表(LOG_TAB)不变。该类型触发器属于多个事件的触发器。修改后的脚本如下:

01  CREATE TRIGGER PRODUCTINFO_OPER_DML_TGR
02    AFTER INSERT OR UPDATE OR DELETE 
03    ON PRODUCTINFO
04    FOR EACH ROW
05  BEGIN
06    CASE
07      WHEN INSERTING THEN     --增加操作
08        INSERT INTO LOG_TAB
09        VALUES
10          (LOG_TAB_ID.NEXTVAL,  
11           'PRODUCTINFO',
12           'INSERT',
13           :NEW.PRODUCTID,
14           SYSDATE);
15        DBMS_OUTPUT.PUT_LINE('插入数据完成,主键是 ' || :NEW.PRODUCTID);
16      WHEN UPDATING THEN      --修改操作
17        INSERT INTO LOG_TAB
18        VALUES
19          (LOG_TAB_ID.NEXTVAL,
20           'PRODUCTINFO',
21           'UPDATE',
22           :OLD.PRODUCTID,
23           SYSDATE);
24        DBMS_OUTPUT.PUT_LINE('修改数据完成,修改数据主键是 ' || :OLD.PRODUCTID);
25      WHEN DELETING THEN      --删除操作
25        INSERT INTO LOG_TAB
27        VALUES
28        (LOG_TAB_ID.NEXTVAL,
29         'PRODUCTINFO',
30         'DELETE',
31         :OLD.PRODUCTID,
32         SYSDATE);
33      DBMS_OUTPUT.PUT_LINE('删除数据完成,删除数据主键是 ' || :OLD.PRODUCTID);
34    END CASE;
35  END;
36  /

【代码解析】
■ 第1行表示创建名为PRODUCTINFO_OPER_DML_TGR的触发器。
■ 第2行表示在增加、修改或删除操作后触发。
■ 第3行表示该触发器作用在PRODUCTINFO表上。
■ 第4行表示行级触发。
■ 第6行表示使用CASE语句区分各种操作。
■ 第7~15行表示当增加数据时,向事件记录表中增加数据并输出到屏幕。其中LOG_TAB_ID.NEXTVAL表示利用序列作为新增数据的主键,:NEW.PRODUCTID表示新增数据的产品ID。
■ 第16~24行表示当修改数据时,向事件记录表中增加数据并输出到屏幕。:OLD.PRODUCTID表示修改前该字段的值。
■ 第25~32行表示当删除数据时,向事件记录表中增加数据并输出到屏幕。:OLD.PRODUCTID表示删除前该字段的值。

【验证触发器】

该触发器是行级触发器,当修改或删除多条记录时会触发多次,而且如果没有数据被修改,即使执行删除或修改操作都不会激发该触发器。在表PRODUCTINFO上分别执行增加、修改、删除操作,查看该触发器是否被正常触发。具体操作步骤如下: 

1)对PRODUCTINFO表执行DML操作。为PRODUCTINFO表增加一条记录,脚本如下:

INSERT INTO PRODUCTINFO VALUES('0240090001','触发器测试',1000,0,0000000000,'','测试');

修改PRODUCTINFO表记录,把所有的DESPERATION字段值置成TEST字符串,脚本如下:

UPDATE PRODUCTINFO SET DESPERATION = 'TEST';

删除表PRODUCTINFO中的一条记录,脚本如下:

DELETE FROM PRODUCTINFO WHERE PRODUCTID = '0240090001';

以上3条语句执行完成后提示如下图所示:

2)检查事件记录表的数据是否成功。执行如下SQL查询语句,查询结果如下图所示。

对比两图,发现操作是对应的,也就是说该触发器成功地激发了。该例利用了CASE语句对3种不同触发方式分别进行操作,当然也可以利用IF语句实现同样的功能。其中在执行UPDATE操作的时候修改了多条记录,因为是行级触发,那么该触发器会触发多次,把所有修改的数据的主键都得到了,并把相关数据放进了事件记录表。

 

3、语句级触发器

【示例4】语句级触发器

该示例将通过语句级触发器控制每个月的1号晚上23点到0点不允许操作表中数据。具体脚本如下:

01  CREATE TRIGGER PRODUCTINFO_STMT_CHK_TGR
02    BEFORE INSERT OR UPDATE OR DELETE ON PRODUCTINFO
03  BEGIN
04    IF (TO_CHAR(SYSDATE, 'dd') = 1 AND TO_CHAR(SYSDATE, 'HH24') = '23') THEN
05      RAISE_APPLICATION_ERROR(-20000, '当前时段不允许修改数据!');
06    END IF;
07  END;

【代码解析】
■ 第1行表示创建名为PRODUCTINFO_STMT_CHK_TGR的触发器。
■ 第2行表示该触发器在增加、修改、删除前触发。
■ 第4行表示判断时间是否为每月的1号23∶00~0∶00,如果是,则不允许修改数据。
■ 第5行表示把应用程序的错误提交到客户端。

【验证触发器】
验证该触发器时可以向PRODUCTINFO表增加、修改或删除数据,这里以增加数据为例测试触发器。SQL语句如下:

INSERT INTO PRODUCTINFO VALUES('0240090001','触发器测试',1000,0,0000000000,NULL,'测试');

执行效果如下图:

利用语句级触发器可以达到在某个条件范围内用户对表数据操作进行限制的目的。

4、触发器执行顺序

在同一对象上可以设置多个触发器,因此触发器被激发的顺序要有先后关系,其触发顺序如下:
1)首先被触发的将是前语句触发器(before statement trigger),该触发器会被执行一次。
2)如果有行级的触发器则接下来执行前行级触发器(before row trigger),行级触发器执行的次数同SQL修改的记录次数一致。
3)当SQL修改记录完成后会触发行级触发器,这时候的行级触发器为后行级触发器(after row trigger),该类型触发的次数同SQL修改记录的次数一致。
4)执行一次语句级的触发器,此时的语句级触发器为后语句级触发器(after statement trigger)。
在Oracle 11g之前,如果有多个相同类型的、相同事件触发器作用在同一个对象上,那么触发器最终被执行的顺序会有一定的随机性,而在Oracle 11g中利用可以FOLLOWS控制其顺序。

5、复合型触发器

复合型的触发器是Oracle 11g的新特性,属于触发器的增强部分。复合型的触发器相当于在一个触发器中包含了4种不同类型的触发器,分别是语句之前(before statement)、行之前(before row)、行之后(after row)、语句之后(after statement)。这么做可以很轻松地在各状态之间传递变量,而在该类型触发器出现之前,变量在触发器之间的传递比较麻烦。
利用该类型的触发器还可以方便地解决ORA-04091错误,这里涉及一个变异表的概念,读者可以理解变异表是正在被DML操作修改的表,也是触发器的作用表。而触发器通常不能对变异表进行操作,下面一个示例将利用复合类型的触发器,解决ORA-04091错误。


【示例5】复合型触发器
该触发器试图实现的功能为:当为PRODUCTINFO表中的数据提高价格时,触发器判断新旧价格差是否高于价格在2000以下的所有产品价格的平均值的20%,如果高于此值,则提示数据有问题。相关步骤如下:
1)创建普通类型的触发器并激发,查看是否存在ORA-04091错误。具体脚本如下:

01  CREATE TRIGGER PRIC_TGR
02    AFTER UPDATE ON PRODUCTINFO
03    FOR EACH ROW
04  DECLARE
05    V_PRO_AVG NUMBER(10, 2) := 0.0;
06  BEGIN
07    SELECT AVG(PRODUCTPRICE)
08      INTO V_PRO_AVG
09      FROM PRODUCTINFO
10     WHERE PRODUCTINFO.PRODUCTPRICE < 2000;
11  
12    IF :NEW.PRODUCTPRICE - :OLD.PRODUCTPRICE > V_PRO_AVG * 0.20 THEN
13      RAISE_APPLICATION_ERROR(-20001, '数据修改错误!');
14    END IF;
15  END;

【代码解析】
■ 第1~3行表示创建作用于表PRODUCTINFO的修改后的行级触发器,名称为PRIC_TGR。
■ 第7~10行表示求取价格在2000以下的产品的平均价格,并存入变量V_PRO_AVG中,这种SELECT INTO形式的语句会把触发表作为变异表。
■ 第12~14行判断新增价格是否高于平均价格的20%,如果高,则提示出错。

【验证触发器】
该触发器创建完成后,执行如下语句:

UPDATE PRODUCTINFO
   SET PRODUCTPRICE = PRODUCTPRICE + 300
 WHERE PRODUCTPRICE > 2000;

执行效果见下图:

2)创建复合类型的触发器,解决上一步出现的错误。具体脚本如下:

01  CREATE OR REPLACE TRIGGER COMPOUND_TGR
02    FOR UPDATE ON PRODUCTINFO COMPOUND TRIGGER
03  
04  V_PRO_AVG NUMBER(10, 2) := 0.0;
05  
06    BEFORE STATEMENT IS
07    BEGIN
08          SELECT AVG(PRODUCTPRICE)
09          INTO V_PRO_AVG
10          FROM PRODUCTINFO
11          WHERE PRODUCTINFO.PRODUCTPRICE < 2000;
12    END BEFORE STATEMENT;
13  
14    AFTER EACH ROW IS
15    BEGIN 
16      IF :NEW.PRODUCTPRICE - :OLD.PRODUCTPRICE > V_PRO_AVG * 0.20 THEN 
17        RAISE_APPLICATION_ERROR(-20011, '数据修改错误!');
18      END IF; 
19      END  AFTER EACH ROW;
20  END;
21  /

代码解析】
■ 第1~2行表示创建复合类型的触发器,触发事件为UPDATE操作。
■ 第4行表示声明变量V_PRO_AVG,用于存放平均价格。
■ 第6~12行表示前语句触发的脚本。此时的操作相当于前语句触发器的操作。
■ 第14~19行表示后行级的触发。也就是说,它的执行次数和修改的记录数一致。

验证触发器】
该触发器创建完成后,执行如下语句:

UPDATE PRODUCTINFO
   SET PRODUCTPRICE = PRODUCTPRICE + 300
 WHERE PRODUCTPRICE > 2000;

执行效果见下图:

从图中可以看出该触发器已经正常激发,并且避免了普通触发器出现的ORA-04091错误,以后小伙伴们就可以不用费神地解决此类错误了。

6、 INSTEAD OF类型触发器

在该类型的触发器作用下,如果对作用对象执行DML操作,那么该操作会被触发器的内部操作所取代。触发器作用在视图当中,用于解决视图不可更新的问题。至于什么样的视图不可更新,小伙伴们可以参考视图篇内容。

【示例6】INSTEAD OF触发器的使用
该示例将演示利用INSTEAD OF触发器解决视图的不可更新问题。实现步骤如下:                                                                                   

1)创建视图。具体脚本如下:

CREATE VIEW PRODUCTINFO_VIEW AS                      ---视图
SELECT DISTINCT PRODUCTNAME, PRODUCTPRICE, QUANTITY, CATEGORY , ORIGIN
   FROM PRODUCTINFO;

2)创建触发器,具体脚本如下:

01     CREATE TRIGGER INSTEAD_OF_TGR  --触发器
02       INSTEAD OF INSERT ON PRODUCTINFO_VIEW
03     DECLARE
04       CATEGID VARCHAR2(10);    --产品类型编码
05     BEGIN
06       SELECT CATEGORYID
07         INTO CATEGID
08         FROM CATEGORYINFO
09        WHERE CATEGORYINFO.CATEGORYNAME = :NEW.CATEGORY;
10       DBMS_OUTPUT.PUT_LINE('-----' || CATEGID);
11       INSERT INTO PRODUCTINFO
12       VALUES
13         ('0240090001',
14          :NEW.PRODUCTNAME,
15          :NEW.PRODUCTPRICE,
16          :NEW.QUANTITY,
17          CATEGID,
18          '测试',
19          :NEW.ORIGIN);
20     END;
21    /

【代码解析】
■ 第1~2行表示创建替代类型的触发器作用在视图PRODUCTINFO_VIEW上,触发事件为INSERT事件,触发器名称为INSTEAD_OF_TGR。
■ 第4行表声明变量CATEGID,用于用做存储产品类型编码。
■ 第6~9行表示根据输入的产品类型名称查询产品类型编码,并把该值存入变量CATEGID中。这步操作有可能引发异常,前面章节都已有相关介绍,这里不再给出。
■ 第11~19行表示向表PRODUCTINFO中增加数据,该增加操作是这个触发器的主要操作。这部分的SQL语句替换了针对视图的增加操作,达到了解决视图不可更新问题的目的。其中第14、15、16、19行使用了对视图执行DML操作语句中的值。

【验证触发器】
以上步骤操作完成后,可以对视图执行增加操作,具体的SQL脚本如下:

INSERT INTO PRODUCTINFO_VIEW VALUES('触发器测试',1000,0,'雨具','中国');

该脚本执行成功后,可对PRODUCTINFO表进行查询,查看该触发器是否成功把数据添加到表中。增加数据以及查询结果如下图所示。

从图中标示部分可以看出已经把数据插入到表PRODUCTINFO中。其中CATEGORY字段值对应CATEGORYID表中的雨具编码。需要注意的是,该类型的触发器只能用于行级的触发。

7、DDL类型触发器

用DDL类型的触发器可以限制和记录特定的DDL操作。例如,通过DDL触发器可以限制对数据库结构的修改,记录数据库中的更改事件,也可以在修改对象的时候根据实际情况作出其他相应动作。下面通过一个示例演示如何创建和使用该类型的触发器。

【示例7】使用DDL类型触发器

该触发器将提示CREATE创建操作,如果有人执行了TEST表的删除操作,将提示错误,同时也不允许对表使用ALTER操作和RENAME操作。具体脚本如下:

01  CREATE TRIGGER DDL_TGR
02    BEFORE CREATE OR ALTER OR DROP OR RENAME ON SCHEMA
03  BEGIN
04    IF SYSEVENT = 'CREATE' THEN
05      DBMS_OUTPUT.PUT_LINE(DICTIONARY_OBJ_NAME || '创建中。。。');
06    ELSIF SYSEVENT = 'DROP' THEN
07      IF DICTIONARY_OBJ_NAME = 'TEST' THEN
08        RAISE_APPLICATION_ERROR(-20000, '不允许删除TEST表!');
09      END IF;
10    ELSIF SYSEVENT = 'ALTER' THEN
11      RAISE_APPLICATION_ERROR(-20000, '不允许修改表!');
12    ELSIF SYSEVENT = 'RENAME' THEN
13      RAISE_APPLICATION_ERROR(-20000, '不允许修改表名称!');  
14    END IF;
15  END;
16  /

【代码解析】
■ 第1~2行表示在模式上创建名为DDL_TGR的触发器,该触发器的触发事件为CREATE、ALTER、DROP、RENAME。
■ 第4~5行表示利用SYSEVENT事件属性得到当前的DDL操作并进行判断。如果为CREATE事件,则输出相关信息。
■ 第7行表示利用DICTIONARY_OBJ_NAME事件属性得到并判断删除的表名是否为TEST,如果条件为TRUE,则提示出错。
■ 第10~14行利用同样的方式进行判断。

【验证触发器】
以上步骤操作完成后,对表TEST执行重命名操作。具体SQL脚本如下:

RENAME  TEST TO  TEST;

例中只用了一种验证方式,感兴趣的读者可以试着使用其他3种方式进行验证。这里使用了事件属性,它们是SYS拥有的独立函数。利用事件属性可以获取触发事件的信息,下面列出了常用的几种事件属性,如下表

还有 “用户和系统事件触发器”等,因为使用较少,这里不过多介绍。

到此,触发器学习告一段落,欢迎纠正,欢迎补充。

本博客主要引自:《零基础学Oracle》 — 赵雪 胡可 王建强

最后

以上就是忧伤鸭子为你收集整理的Oracle触发器(Trigger)-------------保证数据的正确性一、什么是触发器二、触发器的作用三、触发器类型四、触发器的语法五、触发器的使用的全部内容,希望文章能够帮你解决Oracle触发器(Trigger)-------------保证数据的正确性一、什么是触发器二、触发器的作用三、触发器类型四、触发器的语法五、触发器的使用所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(55)

评论列表共有 0 条评论

立即
投稿
返回
顶部