`
jsamson
  • 浏览: 116920 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

触发器TRIGGER-记录

 
阅读更多

直接上例子:

 

drop table web_org_oper_history;

 

create table web_org_oper_history(

autoId number primary key,

c_oper_id varchar2(50) not null,

c_passwd varchar2(32),

oper_date timestamp

);

comment on column web_org_oper_history.c_oper_id is 'ID';

comment on column web_org_oper_history.c_passwd is '密码';

 

CREATE SEQUENCE seq_trg_oper_autoid;

 

select * from web_org_oper_history;

 

 

---触发器

create or replace trigger web_org_oper_triger

       after insert or update or delete on web_org_oper

       for each row 

declare

     pragma AUTONOMOUS_TRANSACTION;

     l_found boolean;

     code NUMBER;

     msg  VARCHAR2(500);

begin

     if length(:new.c_passwd)>6 then--测试

        raise_application_error(-20000, '密码长度超过6字节');

      end if;

     if inserting and :new.c_oper_id is not null then

        insert into web_org_oper_history(autoId,c_oper_id,c_passwd,oper_date)

        values(seq_trg_oper_autoid.NEXTVAL,:new.c_oper_id,:new.c_passwd,sysdate); 

        Dbms_output.put_line('增加操作');

        

     elsif updating and  :new.c_passwd is not null then

        l_found :=false;

        for c1 in(select c_oper_id from web_org_oper_history where c_oper_id=:new.c_oper_id and c_passwd=:old.c_passwd) loop

         l_found :=true;

         exit;

         end  loop;

         if not l_found then

            insert into web_org_oper_history(autoId,c_oper_id,c_passwd,oper_date)

                    values(seq_trg_oper_autoid.NEXTVAL,:new.c_oper_id,:new.c_passwd,sysdate); 

             Dbms_output.put_line('增加操作');

         else

            update web_org_oper_history set c_passwd=:new.c_passwd,oper_date=sysdate where c_oper_id=:new.c_oper_id;

            Dbms_output.put_line('更新操作');

            /*insert into web_org_oper_history(c_oper_id,c_passwd)

                    values(:new.c_oper_id,:new.c_passwd); */

          end if;

      end if;

      commit;

      /*下面异常捕获 对上面判断不返回到应用程序 注释掉即可返回到应用*/

      EXCEPTION

          WHEN OTHERS THEN

               code := SQLCODE;

               msg  := substr(SQLERRM, 1, 500);

               Dbms_output.put_line(code);

               Dbms_output.put_line(msg);

               ROLLBACK;

               RAISE;

end web_org_oper_triger;

 

instead of触发器 转自文档:http://blog.sina.com.cn/s/blog_7cc3b8db010113oj.html

下面介绍一种instead of触发器,该触发器主要使用在对视图的更新上,以下是instead of触发器的语法:
CREATE OR REPLACE TRIGGER trigger_name
INSTEAD OF <insert | update | delete> ON view_name
[FOR EACH ROW]
WHEN (condition)
DECLARE
BEGIN
 --触发器代码
END;

其他部分语法同前面所述的before和after语法是一样的,唯一不同的是在第二行用上了instead of关键字。对于普通的视图来说,进行insert等操作是被禁止的,因为Oracle无法知道操作的字段具体是哪个表中的字段。但我们可以通过建立instead of触发器,在触发器主体中告诉Oracle应该更新,删除或者修改哪些表的哪部分字段。如:

6,例三:instead of触发器
新建视图
CREATE VIEW employee_salary(employee_id, maxsalary, MONTH, amount) AS
SELECT a.employee_id, a.maxsalary, b.MONTH, b.amount
FROM employment a, salary b
WHERE a.employee_id = b.employee_id

如果执行插入语句
INSERT INTO employee_salary(employee_id, maxsalary, MONTH, amount)
VALUES(10, 100000, '200606', 10000);
系统会报错:
ORA-01779:无法修改与非键值保存表对应的列

我们可以通过建立以下的instead of存储过程,将插入视图的值分别插入到两个表中:
create or replace trigger employee_salary_rii
  instead of insert on employee_salary 
  for each ROW
DECLARE
 v_cnt NUMBER;
BEGIN
  --检查是否存在该员工信息
 SELECT COUNT(*)
  INTO v_cnt
  FROM employment
  WHERE employee_id = :NEW.employee_id;
 IF v_cnt = 0 THEN
  INSERT INTO employment
   (employee_id, maxsalary)
  VALUES
   (:NEW.employee_id, :NEW.maxsalary);
 END IF;
  --检查是否存在该员工的工资信息
 SELECT COUNT(*)
  INTO v_cnt
  FROM salary
  WHERE employee_id = :NEW.employee_id
   AND MONTH = :NEW.MONTH;
 IF v_cnt = 0 THEN
  INSERT INTO salary
   (employee_id, MONTH, amount)
  VALUES
   (:NEW.employee_id, :NEW.MONTH, :NEW.amount);
 END IF;
END employee_salary_rii;

该触发器被建立后,执行上述insert操作,系统就会提示成功插入一条记录。
但需要注意的是,这里的“成功插入一条记录”,只是Oracle并未发现触发器中有异常抛出,而根据insert语句中涉及的记录数作出一个判断。若触发器的主体什么都没有,只是一个空语句,Oracle也会报“成功插入一条记录”。同样道理,即使在触发器主体里往多个表中插入十条记录,Oracle的返回也是“成功插入一条记录”。

 


行级触发器可以解决大部分的问题,但是如果需要对本表进行扫描检查,比如要检查总的工资是否超限了,用行级触发器是不行的,因为行级触发器主体中不能有涉及到关联表的事务,这时就需要用到语句级触发器。以下是语句级触发器的语法:
CREATE OR REPLACE TRIGGER trigger_name
<before | after | instead of ><insert | update | delete > ON table_name
DECLARE
BEGIN
 --触发器主体
END;

从语法定义上来看,行级触发器少了for each row,也不能使用when子句来限定入口条件,其他部分都是一样的,包括insert, update, delete和instead of都可以使用。


7,例四:语句级触发器之一
CREATE OR REPLACE TRIGGER salary_saiu
AFTER INSERT OR UPDATE OF amount ON salary
DECLARE
 v_sumsalary NUMBER;
BEGIN
  SELECT SUM(amount) INTO v_sumsalary FROM salary;
 IF v_sumsalary > 500000 THEN
  raise_application_error(-20001, '总工资超过500000');
 END IF;
END;

以上代码定义了一个语句级触发器,该触发器检查在insert和update了amount字段后操作后,工资表中所有工资记录累加起来是否超过500000,如果超过则抛出异常。从这个例子可以看出,语句级触发器可以对关联表表进行扫描,扫描得到的结果可以用来作为判断一致性的标志。需要注意的是,在before语句触发器主体和after语句触发器主体中对关联表进行扫描,结果是不一样的。在before语句触发器主体中扫描,扫描结果将不包括新插入和更新的记录,也就是说当以上代码换成 before触发器后,以下语句将不报错:
INSERT INTO salary(employee_id, month, amount) VALUEs(2, '200601', 600000)
这是因为在主体中得到的v_sumsalary并不包括新插入的600000工资。
另外,在语句级触发器中不能使用:new和:old对象,这一点和行级触发器是显著不同的。如果需要检查插入或更新后的记录,可以采用临时表技术。
临时表是一种Oracle数据库对象,其特点是当创建数据的进程结束后,进程所创建的数据也随之清除。进程与进程不可以互相访问同一临时表中对方的数据,而且对临时表进行操作也不产生undo日志,减少了数据库的消耗。具体有关临时表的知识,可以参看有关书籍。
为了在语句级触发器中访问新插入后修改后的记录,可以增加行级触发器,将更新的记录插入临时表中,然后在语句级触发器中扫描临时表,获得修改后的记录。临时表的表结构一般与关联表的结构一致。


8,例五:语句级触发器之二
目的:限制每个员工的总工资不能超过50000,否则停止对该表操作。
创建临时表
create global temporary table SALARY_TMP
(
  EMPLOYEE_ID NUMBER,
  MONTH       VARCHAR2(6),
  AMOUNT      NUMBER
)
on commit delete rows;

为了把操作记录插入到临时表中,创建行级触发器:
CREATE OR REPLACE TRIGGER salary_raiu
AFTER INSERT OR UPDATE OF amount ON salary
FOR EACH ROW
BEGIN
  INSERT INTO salary_tmp(employee_id, month, amount)
  VALUES(:NEW.employee_id, :NEW.MONTH, :NEW.amount);
END;
该触发器的作用是把更新后的记录信息插入到临时表中,如果更新了多条记录,则每条记录都会保存在临时表中。

创建语句级触发器:
CREATE OR REPLACE TRIGGER salary_sai
AFTER INSERT OR UPDATE OF amount ON salary
DECLARE
 v_sumsalary NUMBER;
BEGIN
 FOR cur IN (SELECT * FROM salary_tmp) LOOP
  SELECT SUM(amount)
   INTO v_sumsalary
   FROM salary
   WHERE employee_id = cur.employee_id;
  IF v_sumsalary > 50000 THEN
   raise_application_error(-20002, '员工累计工资超过50000');
  END IF;
    DELETE FROM salary_tmp;
 END LOOP;
END;

该触发器首先用游标从salary_tmp临时表中逐条读取更新或插入的记录,取employee_id,在关联表salary中查找所有相同员工的工资记录,并求和。若某员工工资总和超过50000,则抛出异常。如果检查通过,则清空临时表,避免下次检查相同的记录。
执行以下语句:
INSERT INTO salary(employee_id, month, amount) VALUEs(7, '200601', 20000);
INSERT INTO salary(employee_id, month, amount) VALUEs(7, '200602', 20000);
INSERT INTO salary(employee_id, month, amount) VALUEs(7, '200603', 20000);
在执行第三句时系统报错:
ORA-20002:员工累计工资超过50000
查询salary表,发现前两条记录正常插入了,第三条记录没有插入。


如果系统结构比较复杂,而且触发器的代码比较多,在触发器主体中写过多的代码,对于维护来说是一个困难。这时可以将所有触发器的代码写到同一个包中,不同的触发器代码以不同的存储过程封装,然后触发器主体中调用这部分代码。

9,例六:用包封装触发器代码
目的:改写例五,封装触发器主体代码
创建代码包:
CREATE OR REPLACE PACKAGE BODY salary_trigger_pck IS

 PROCEDURE load_salary_tmp(i_employee_id IN NUMBER,
       i_month       IN VARCHAR2,
       i_amount      IN NUMBER) IS
 BEGIN
  INSERT INTO salary_tmp VALUES (i_employee_id, i_month, i_amount);
 END load_salary_tmp;

 PROCEDURE check_salary IS
  v_sumsalary NUMBER;
 BEGIN
  FOR cur IN (SELECT * FROM salary_tmp) LOOP
   SELECT SUM(amount)
    INTO v_sumsalary
    FROM salary
    WHERE employee_id = cur.employee_id;
   IF v_sumsalary > 50000 THEN
    raise_application_error(-20002, '员工累计工资超过50000');
   END IF;
   DELETE FROM salary_tmp;
  END LOOP;
 END check_salary;
END salary_trigger_pck;
包salary_trigger_pck中有两个存储过程,load_salary_tmp用于在行级触发器中调用,往salary_tmp临时表中装载更新或插入记录。而check_salary用于在语句级触发器中检查员工累计工资是否超限。

修改行级触发器和语句级触发器:
CREATE OR REPLACE TRIGGER salary_raiu
 AFTER INSERT OR UPDATE OF amount ON salary
 FOR EACH ROW
BEGIN
 salary_trigger_pck.load_salary_tmp(:NEW.employee_id,  :NEW.MONTH, :NEW.amount);
END;

CREATE OR REPLACE TRIGGER salary_sai
AFTER INSERT OR UPDATE OF amount ON salary
BEGIN
 salary_trigger_pck.check_salary;
END;

这样主要代码就集中到了salary_trigger_pck中,触发器主体中只实现了一个调用功能。

10,触发器命名规范
为了方便对触发器命名和根据触发器名称了解触发器含义,需要定义触发器的命名规范:
Trigger_name = table_name_trg_<R|S><A|B|I><I|U|D>

触发器名限于30个字符。必须缩写表名,以便附加触发器属性信息。
<R|S>基于行级(row)还是语句级(statement)的触发器
<A|B|I>after, before或者是instead of触发器
<I|U|D>触发事件是insert,update还是delete。如果有多个触发事件则连着写

例如:
Salary_rai  salary表的行级after触发器,触发事件是insert
Employee_sbiud employee表的语句级before触发器,触发事件是insert,update和delete

分享到:
评论

相关推荐

    SQL语句,存储过程,触发器

    SQL语句,存储过程,触发器

    Oracle触发器trigger详解

    触发器相关概念及语法 概述 本篇博文中主要探讨以下内容: ...当员工表中新增一条记录后,自动打印“成功插入新员工” create or replace trigger insertStaffHint after insert on xgj_test for each row d

    sqlserver触发器例子

    一﹕ 触发器是一种特殊的存储过程﹐它不能被显式地调用﹐而是在往表中插入记录﹑更新记录或者删除记录时被自动地激活。所以触发器可以用来实现对表实施复杂的完整性约`束。 二﹕ SQL Server为每个触发器都创建了...

    sql触发器实例详细讲解

    Create Trigger truStudent On Student --在Student表中创建触发器 for Update --为什么事件触发 As --事件触发后所要做的事情 if Update(StudentID) begin Update BorrowRecord Set StudentID=i....

    oracle触发器实例讲解

    oracle触发器实例讲解2008-11-27 09:17--[6]// Oracle Trigger ---------------------------------------------------------------------------------------------// --实例1------------------------ --创建触发器...

    SQL_Server-trigger.rar_trigger_创建触发器

    SQL Server触发器创建、删除、修改、查看示例教程...触发器是一种特殊的存储过程﹐它不能被显式地调用﹐而是在往表中插入记录﹑更新记录或者删除记录时被自动地激活。所以触发器可以用来实现对表实施复杂的完整性约束。

    实验六-触发器实验报告.doc

    建立触发器 CREATE [OR REPLACE] TRIGGER &lt;触发器名&gt; BEFORE"AFTER INSERT"DELETE"UPDATE OF &lt;列名&gt; ON &lt;表名&gt; [FOR EACH ROW] WHEN (&lt;条件&gt;) 程序块&gt; ON 子句中的名称识别与数据库触发器关联的数据库表 触发器事件...

    sakila数据库触发器的理解 基于sakila的触发器的设计

    (注意:请将创建语句、调用结果截图记录到实验报告里) 4.1. 设计 1 个视图,至少关联 3 个表; 4.2设计 1 个触发器,需要在报告里体现触发器生效。 PDF中内容仅包含个人意见,欢迎交流和讨论,如有错误还望指出

    Oraclet中的触发器

    例1: 建立一个触发器, 当职工表 emp 表被删除一条记录时,把被删除记录写到职工表删除日志表中去。 CREATE TABLE emp_his AS SELECT * FROM EMP WHERE 1=2; CREATE OR REPLACE TRIGGER del_emp BEFORE DELETE ON ...

    MySQL数据库:触发器举例.pptx

    因为是修改了Sell表的记录后才执行触发器程序修改Book表中的记录,此时Sell表中该记录已经修改了,所以只能用NEW.图书编号来表示这个修改后的记录的图书编号,Book表使用WHERE 图书编号=NEW.图书编号条件查找要修改...

    Oracle 使用触发器创建自动序列号

    --建表 drop table TEST create table TEST ...--测试 insert条语句将会自动生成自定义主键,即插入其他字段,主键在插入记录前自动生成 insert into test(test1) values('bbb'); select * from test;

    PostgreSQL recover deleted rows trigger:PostgreSQL恢复已删除的行触发器-开源

    防止使用此实用程序(安装后)永久删除记录,该实用程序使您可以恢复任何表上已删除的记录,无论数据类型如何。 如果表的结构发生了更改(添加或删除字段),则从该表中删除记录后,fn_RecoverReg函数可以使用1字段...

    SQL 基于触发器和游标的项目应用

    触发器(Trigger)是一种特殊的存储过程,用户不能直接调用,它是一个功能强大的数据库对象,它可以在有数据修改时自动强制执行相应的业务规则。 游标提供了一种对从表中检索出的数据进行操作的灵活手段,实际上是...

    一个DDL触发器例子(阻止DDL并记警告日志)

    有时我们需要控制用户对表执行DDL操作,包括truncate等操作。为了达到灵活控制的目的,我们使用了DDL trigger

    NiNiTechnology:ff14时间轴和触发器

    NiNiTechnology温馨提示:ff14国际服版本5.5及后续版本的尼尼科技Triggernometry触发器将分为普通版和专业版两种不同版本。专业版触发器需使用尼尼科技专业版增强触发插件Triggernometry-pro导入( )专业版触发器...

    (mysql面试题)MySQL中的存储过程和触发器的概念及其作用及代码展示.txt

    - 然后,我们使用`CREATE TRIGGER`命令创建一个名为`trg_student_count_after_insert`的触发器,该触发器在`students`表插入新记录后执行。触发器的功能是统计新插入学生的数量,并调用`GetStudentCountByGrade`存储...

    Oracle触发器备份表数据

    1.通过行触发器备份表数据  – 创建备份表,首次将数据全部备份 ...  – 创建行触发器:当emp2有数据被删除时,将数据备份到emp2_bak表中,并记录删除时间  create or replace trigger autoBakEmp2  before

    Oracle数据库(触发器、存储过程、函数、包)

    知识点一、触发器(trigger)  推荐练习文档:  http://wenku.baidu.com/link?url=brtd9myO4XIaEjRPaRB0yWgV2_TfXmNxS6KKjB_avq7d0hPs3SknJdrs-JLtWe–H-f3q-I61HUxkcqqAFMC0z6Zf65QBbmFRB8TAlULmGS  ...

    sql 触发器使用例子

    inserted,deleted是在触发器中使用的两个临时表,当执行insert操作时,在inserted中存储着当前插入的记录,在执行delete操作时,在deleted中存储着当前删除的记录,当执行update时,在inserted中存储着修改后的记录,...

    MySQL笔记之触发器的应用

    FOR EACH ROW表示任何一条记录上的操作满足触发事件都会触发该触发器 代码如下:mysql&gt; CREATE TRIGGER trig1 AFTER INSERT -&gt; ON work FOR EACH ROW -&gt; INSERT INTO time VALUES(NOW());Query OK,

Global site tag (gtag.js) - Google Analytics