直接上例子:
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语句,存储过程,触发器
触发器相关概念及语法 概述 本篇博文中主要探讨以下内容: ...当员工表中新增一条记录后,自动打印“成功插入新员工” create or replace trigger insertStaffHint after insert on xgj_test for each row d
一﹕ 触发器是一种特殊的存储过程﹐它不能被显式地调用﹐而是在往表中插入记录﹑更新记录或者删除记录时被自动地激活。所以触发器可以用来实现对表实施复杂的完整性约`束。 二﹕ SQL Server为每个触发器都创建了...
Create Trigger truStudent On Student --在Student表中创建触发器 for Update --为什么事件触发 As --事件触发后所要做的事情 if Update(StudentID) begin Update BorrowRecord Set StudentID=i....
oracle触发器实例讲解2008-11-27 09:17--[6]// Oracle Trigger ---------------------------------------------------------------------------------------------// --实例1------------------------ --创建触发器...
SQL Server触发器创建、删除、修改、查看示例教程...触发器是一种特殊的存储过程﹐它不能被显式地调用﹐而是在往表中插入记录﹑更新记录或者删除记录时被自动地激活。所以触发器可以用来实现对表实施复杂的完整性约束。
建立触发器 CREATE [OR REPLACE] TRIGGER <触发器名> BEFORE"AFTER INSERT"DELETE"UPDATE OF <列名> ON <表名> [FOR EACH ROW] WHEN (<条件>) 程序块> ON 子句中的名称识别与数据库触发器关联的数据库表 触发器事件...
(注意:请将创建语句、调用结果截图记录到实验报告里) 4.1. 设计 1 个视图,至少关联 3 个表; 4.2设计 1 个触发器,需要在报告里体现触发器生效。 PDF中内容仅包含个人意见,欢迎交流和讨论,如有错误还望指出
例1: 建立一个触发器, 当职工表 emp 表被删除一条记录时,把被删除记录写到职工表删除日志表中去。 CREATE TABLE emp_his AS SELECT * FROM EMP WHERE 1=2; CREATE OR REPLACE TRIGGER del_emp BEFORE DELETE ON ...
因为是修改了Sell表的记录后才执行触发器程序修改Book表中的记录,此时Sell表中该记录已经修改了,所以只能用NEW.图书编号来表示这个修改后的记录的图书编号,Book表使用WHERE 图书编号=NEW.图书编号条件查找要修改...
--建表 drop table TEST create table TEST ...--测试 insert条语句将会自动生成自定义主键,即插入其他字段,主键在插入记录前自动生成 insert into test(test1) values('bbb'); select * from test;
防止使用此实用程序(安装后)永久删除记录,该实用程序使您可以恢复任何表上已删除的记录,无论数据类型如何。 如果表的结构发生了更改(添加或删除字段),则从该表中删除记录后,fn_RecoverReg函数可以使用1字段...
触发器(Trigger)是一种特殊的存储过程,用户不能直接调用,它是一个功能强大的数据库对象,它可以在有数据修改时自动强制执行相应的业务规则。 游标提供了一种对从表中检索出的数据进行操作的灵活手段,实际上是...
有时我们需要控制用户对表执行DDL操作,包括truncate等操作。为了达到灵活控制的目的,我们使用了DDL trigger
NiNiTechnology温馨提示:ff14国际服版本5.5及后续版本的尼尼科技Triggernometry触发器将分为普通版和专业版两种不同版本。专业版触发器需使用尼尼科技专业版增强触发插件Triggernometry-pro导入( )专业版触发器...
- 然后,我们使用`CREATE TRIGGER`命令创建一个名为`trg_student_count_after_insert`的触发器,该触发器在`students`表插入新记录后执行。触发器的功能是统计新插入学生的数量,并调用`GetStudentCountByGrade`存储...
1.通过行触发器备份表数据 – 创建备份表,首次将数据全部备份 ... – 创建行触发器:当emp2有数据被删除时,将数据备份到emp2_bak表中,并记录删除时间 create or replace trigger autoBakEmp2 before
知识点一、触发器(trigger) 推荐练习文档: http://wenku.baidu.com/link?url=brtd9myO4XIaEjRPaRB0yWgV2_TfXmNxS6KKjB_avq7d0hPs3SknJdrs-JLtWe–H-f3q-I61HUxkcqqAFMC0z6Zf65QBbmFRB8TAlULmGS ...
inserted,deleted是在触发器中使用的两个临时表,当执行insert操作时,在inserted中存储着当前插入的记录,在执行delete操作时,在deleted中存储着当前删除的记录,当执行update时,在inserted中存储着修改后的记录,...
FOR EACH ROW表示任何一条记录上的操作满足触发事件都会触发该触发器 代码如下:mysql> CREATE TRIGGER trig1 AFTER INSERT -> ON work FOR EACH ROW -> INSERT INTO time VALUES(NOW());Query OK,