百度首页 | 百度空间
 
查看文章
 
oraclle 触发器(4)
2007/09/17 14:24

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


类别:Oracle | 添加到搜藏 | 浏览() | 评论 (0)
 
最近读者:
 
网友评论:
发表评论:
姓 名:
网址或邮箱: (选填)
内 容:
验证码:
 

     

©2008 Baidu