`
jsamson
  • 浏览: 117799 次
  • 性别: 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

分享到:
评论

相关推荐

    触发器---FOR INSERT与INSTEAD OF

    例如,在上面的示例中,我们创建了一个名为tri_tb1的触发器,该触发器在tb1表中插入新记录时执行。触发器的执行顺序是:首先执行INSERT语句,然后执行触发器。 在FOR INSERT触发器中,我们可以访问inserted表,该表...

    oracle forms 触发器执行顺序

    ### Oracle EBS Forms 触发器执行顺序详解 Oracle E-Business Suite (EBS) 是一个集成的企业资源规划 (ERP) 解决方案,它利用了 Oracle 的技术来提供全面的业务流程管理。其中,Oracle Forms 是一个强大的工具,...

    [数据库] Navicat for MySQL触发器更新和插入操作1

    在MySQL中,触发器(Trigger)是一种强大的功能,用于在特定的数据操作(如INSERT、UPDATE、DELETE)发生时自动执行预定义的SQL语句,以此来维护数据的完整性和一致性。触发器可以看作是数据库的自动反应机制,无需...

    SQL触发器实例讲解

    3. 提高效率:触发器可以提高我们处理数据的效率,例如在删除学生的学号时,自动删除借书记录表中的相应记录。 触发器的类型: ---------------- 1. Insert 触发器:在插入数据时触发。 2. Update 触发器:在更新...

    触发器实现sql记录

    ### 触发器实现SQL记录知识点详解 #### 一、背景与需求分析 在很多应用场景中,为了确保数据完整性及可追溯性,我们往往需要记录数据表中的历史变更信息,以便于日后审计或数据分析。例如,在银行系统中,对账户...

    apex-opportunity-trigger-sample:用于验证的 Apex 触发器

    3. **日志和错误处理**:示例可能演示了如何记录触发器执行的日志信息,以及如何优雅地处理错误,避免阻止其他记录的处理。 4. **最佳实践**:代码可能会展示Apex触发器编写的最佳实践,比如使用静态变量来减少...

    触发器实验报告.pdf

    Trigger_sc 触发器是在 SC 表中创建的限制更新触发器,当修改 SC 表中一记录的学号时,要检查 S 中是否存在该学号相同的记录,若存在则不许修改,若不在则可以修改。 C. 用 SQL 方式创建触发器 score_sc_tri score...

    建立触发器-数据库vfvfds

    3. 触发器的创建:使用`CREATE TRIGGER`创建`AFTER UPDATE`触发器,定义触发条件和执行逻辑。 4. 游标的使用:在更新触发器中,利用游标处理批量更新,确保所有受影响的记录都被处理。 5. 触发器测试:通过更新操作...

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

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

    sqlite 触发器

    SQLite触发器是一种特殊的数据库对象,它可以在特定的数据库事件(如删除、插入或更新记录)发生时自动执行一系列预定义的SQL语句。触发器可以帮助开发者实现数据一致性、业务逻辑自动化等功能。 #### 二、SQLite...

    Oracle Form 触发器的执行顺序

    其中,触发器(Trigger)是一种非常重要的组件,它可以根据不同的事件来触发相应的操作。 Oracle Form 触发器的执行顺序是指在不同的事件下触发器的执行过程。在本文中,我们将详细介绍 Oracle Form 触发器的执行...

    mysql触发器使用实例

    - 例如,创建一个在用户插入新记录时自动设置创建日期的触发器: ```sql CREATE TRIGGER set_create_date BEFORE INSERT ON user_table FOR EACH ROW BEGIN SET NEW.create_time = NOW(); END; ``` 在这个...

    触发器(trigger)1

    触发器(Trigger)是数据库系统中的一个重要概念,它是一种特殊类型的存储过程,其主要功能是在特定的数据库操作(如INSERT、DELETE、UPDATE)发生时自动执行。触发器的执行不依赖于应用程序的直接调用,而是由...

    创建监控表的DML的触发器-0422

    3. **触发器**:最为灵活,可以根据复杂的条件记录所需信息。 #### 实现方案:使用触发器 本文将详细介绍如何使用触发器来监控表中的数据修改操作。此方案的核心在于利用Oracle数据库中的系统视图,如`v$session`、...

    Arduino-网络示波器(支持触发器)-项目开发

    `arduino-web-oscilloscope-support-trigger-5d6312.pdf`可能是项目的手册或指南,详细解释了硬件组装、软件配置和触发器设置等步骤。通过阅读这份文档,开发者可以了解到如何构建和配置这个网络示波器,以及如何...

    SQLite触发器的SQL语法.pdf

    SQLite当前只支持FOR EACH ROW类型的触发器,这意味着触发器中的SQL语句将针对每一行记录执行。可以使用`WHEN`子句来限制触发器只在满足特定条件的行上执行。 在触发器的步骤中,可以使用`NEW.column-name`和`OLD....

    oracle触发器实例

    - 行级触发器:在对单条记录进行INSERT、UPDATE或DELETE操作时触发,对每条受影响的记录执行一次。 - 语句级触发器:在执行DML语句后触发,无论操作影响多少记录,只执行一次。 2. 触发事件: - INSERT:在插入...

    SQL触发器语法参考

    在数据库管理系统中,SQL触发器是一种特殊类型的存储过程,它能够自动响应对特定表的数据修改(如插入、更新或删除记录等)。触发器可以在数据修改之前或之后执行,也可以代替数据修改操作本身。本文将详细介绍SQL...

    触发器的使用总结--分享经典

    - **Insert型触发器**:当向表中插入新数据时,触发器会被激活,可用于记录插入前后的状态或执行级联插入等操作。 - **Update型触发器**:在数据更新时触发,可用于验证更新后的数据有效性,或者记录旧值和新值,...

Global site tag (gtag.js) - Google Analytics