Oracle 触发器(一)
1)触发器是一种特殊的存储过程,触发器一般由事件触发并且不能接受参数,存储器由语句块去调用;触发器是当某个事件发生时自动地隐式运行。
2)触发器分类:
1.DML触发器: 创建在表上,由DML事件引发
2.替代触发器(insteadof):由于视图有可能是由多个表进行关联而成,直接通过更新视图来更新基表是不可行的,那我们就可以通过替代触发器来工作了。
3.DDL触发器: 触发事件时数据库对象的创建和修改
4.数据库事件触发器:定义在数据库或者模式上,由数据库事件触发
或者如下分类:
1.行触发器:数据库表中的每一行有变化都会触发一次触发器代码
2.语句触发器:与语句所影响的行数无关,仅触发一次
3.BEFORE触发器:在DML语句执行之前触发
4.ALFTER触发器:在DML语句执行之后触发
3)组成:
1.触发事件:引发触发器被触发的事件 DML语句(INSERT, UPDATE, DELETE语句对表或视图执行数据处理操作)、DDL语句(如CREATE、ALTER、DROP语句在数据库中创建、修改、删除模式对象)、数据库系统事件(如系统启动或退出、异常错误)、用户事件(如登录或退出数据库)。
2.触发时间:即该触发器是在触发事件发生之前(BEFORE)还是之后(AFTER)触发
3.触发操作:触发器触发后要完成的事情
4.触发对象:包括表、视图、模式、数据库。只有在这些对象上发生了符合触发条件的触发事件,触发器才会执行触发操作。
5.触发条件:由WHEN子句指定一个逻辑表达式。只有当该表达式的值为TRUE时,遇到触发事件才会自动执行触发操作。
6.触发频率:说明触发器内定义的动作被执行的次数。即语句级(STATEMENT)触发器和行级(ROW)触发器。(比如delete多条数据时,行级触发器可能会执行多次,语句级触发器只会触发一次)
4)、注意:
1、触发器不能接收参数
2、一张表最多可以有12个触发器
BEFORE INSERT
BEFORE INSERT FOR EACH ROW
AFTER INSERT
AFTER INSERT FOR EACH ROW BEFORE UPDATE
BEFORE UPDATE FOR EACH ROW
AFTER UPDATE
AFTER UPDATE FOR EACH ROW BEFORE DELETE
BEFORE DELETE FOR EACH ROW
AFTER DELETE
AFTER DELETE FOR EACH ROW
3、触发器最大为32k
4、触发器中不能使用数据库事务控制语句,并且由触发器所调用的过程或函数也不能使用数据库事务控制语句,如commit、rollback
5、触发器中不能使用Long
二、语法说明
1、DML触发器:DML触发器指的是在对表进行增删改操作引发的自动执行事件。
--DML触发器基本定义:
CREATE [OR REPLACE] TRIGGER [user.] trigger
{BEFORE|AFTER} {INSERT | DELETE | UPDATE [OF column [, column …]]}
[OR {INSERT | DELETE | UPDATE [OF column [, column …]]}...] on
[user.]table
[FOR EACH ROW]
[WHEN (condition)]
BEGIN
.................
END;
其中:
FOR EACH ROW选项说明触发器为行触发器。
在行触发器的PL/SQL块和WHEN 子句中可以使用相关名称参照当前的新、旧列值,默认的相关名称分别为OLD和NEW。触发器的PL/SQL块中应用相关名称时,必须在它们之前加冒号(:),但在WHEN子句中则不能加冒号。
Demo1:
记录每条记录的插入时间(10g后可以在建表时用rowdependencies实现等级追踪实现效果同样)
--先创建日志记录表
CREATE TABLE SZ_RECORD_TEST
("SZID" VARCHAR2(1024) NOT NULL,
"SZVERSION" VARCHAR2(1024),
"EVENT_TIME" DATE DEFAULT sysdate NOT NULL); --定义event_time的默认值是sysdate;
/ --创建触发器
CREATE OR REPLACE TRIGGER TRG_SZD --触发器名称
AFTER INSERT ON TT_SZD --在TT_SZD表插入表之后出发
FOR EACH ROW --行级触发器
BEGIN
INSERT INTO SZ_RECORD_TEST(SZID,SZVERSION)
VALUES (:NEW.SZD_ID, :NEW.VERSION);
END;
Demo2:
限制对Departments表修改(包括INSERT,DELETE,UPDATE)的时间范围,即不允许在非工作时间修改departments表
CREATE OR REPLACE TRIGGER tr_dept_time
BEFORE INSERT OR DELETE OR UPDATE ON departments --在插入,删除,修改数据时出发
BEGIN
IF (TO_CHAR(sysdate,'DAY') IN ('星期六', '星期日')) OR (TO_CHAR(sysdate, 'HH24:MI') NOT BETWEEN '08:30' AND '18:00') THEN
RAISE_APPLICATION_ERROR(-20001, '不是上班时间,不能修改departments表');
END IF;
END;
/*可能不是很多人知道 RAISE_APPLICATION_ERROR 的用途是什么,虽然从字面上已经猜到这个函数是干什么用的。平时用来测试的异常处理
我们都是通过dbms_output.put_line来输出异常信息,但是在实际的应用中,需要把异常信息返回给调用的客户端。
其实 RAISE_APPLICATION_ERROR 是将应用程序专有的错误从服务器端转达到客户端应用程序(其他机器上的SQLPLUS或者其他前台开发语言)
RAISE_APPLICATION_ERROR 的声明:
PROCEDURE RAISE_APPLICATION_ERROR( error_number_in IN NUMBER, error_msg_in IN VARCHAR2);
里面的错误代码和内容,都是自定义的。说明是自定义,当然就不是系统中已经命名存在的错误类别,是属于一种自定义事务错误类型,才调用此函数。
error_number_in 之容许从 -20000 到 -20999 之间,这样就不会与 ORACLE 的任何错误代码发生冲突。error_msg_in 的长度不能超过 2k,否则截取 2k。*/
Demo3:
限定只对部门号为80的记录进行行触发器操作。
CREATE OR REPLACE TRIGGER tr_emp_sal_comm
BEFORE UPDATE OF salary,
commission_pct OR DELETE ON HR.employees
FOR EACH ROW
WHEN (old.department_id = 80)
BEGIN
CASE WHEN UPDATING ('salary') THEN
IF :NEW.salary < :old.salary THEN
RAISE_APPLICATION_ERROR(-20001, '部门80的人员的工资不能降');
END IF;
WHEN UPDATING ('commission_pct') THEN
IF :NEW.commission_pct < :old.commission_pct THEN
RAISE_APPLICATION_ERROR(-20002, '部门80的人员的奖金不能降');
END IF;
WHEN DELETING THEN
RAISE_APPLICATION_ERROR(-20003, '不能删除部门80的人员记录');
END CASE;
END;
2、替代触发器(insteadof):由于视图有可能是由多个表进行关联而成,直接通过更新视图来更新基表是不可行的,那我们就可以通过替代触发器来工作了。
demo4:
--准备操作
INSERT INTO DEPT(DEPTNO,DNAME,LOC) VALUES(60,'研发部','上海');
/
INSERT INTO EMP(EMPNO, ENAME,JOB,
MGR,HIREDATE,SAL,
COMM,DEPTNO)
VALUES(1001,'贾宝玉','程序员',7788,TO_DATE ('2013-02-03','YYYY-MM-DD'),8000,1000,60);
/
CREATE VIEW emp_info_view
AS
SELECT E.EMPNO,E.ENAME,E.JOB,E.SAL,D.DNAME,D.LOC
FROM EMP e JOIN DEPT d ON e.deptno=d.deptno ;
/
SELECT * FROM emp_info_view; --创建替代触发器
create or replace trigger emp_info_view_trigger
instead of update
on emp_info_view for each row
begin
update dept set dname=:new.dname,loc=:new.loc
where dname=:new.dname;
end;
--更新视图
/
update emp_info_view set loc='杭州' where dname='研发部'
--查询结果
/
SELECT * FROM dept;
CREATE OR REPLACE VIEW emp_view AS
SELECT deptno, count(*) total_employeer, sum(sal) total_salary
FROM emp GROUP BY deptno;
/
在此视图中直接删除是非法:
SQL>DELETE FROM emp_view WHERE deptno=10;
DELETE FROM emp_view WHERE deptno=10
*
ERROR 位于第 1 行:
ORA-01732: 此视图的数据操纵操作非法 --但是我们可以创建INSTEAD_OF触发器来为 DELETE 操作执行所需的处理,即删除EMP表中所有基准行:
/
CREATE OR REPLACE TRIGGER emp_view_delete
INSTEAD OF DELETE ON emp_view FOR EACH ROW
BEGIN
DELETE FROM emp WHERE deptno= :old.deptno;
END emp_view_delete;
/
DELETE FROM emp_view WHERE deptno=10;
3、DDL触发器
DDL触发器,当执行DDL语句时会被触发。按照作用范围,分为schema triggers,database triggers。schema triggers作用在一个用户上,database triggers作用在整个数据库所有用户上。
创建DDL触发器
要创建一个DDL触发器,语法如下:
CREATE [OR REPLACE] TRIGGER trigger name --创建一个触发器并制定名称,or replace是可选项
{BEFORE | AFTER } { DDL event} ON {DATABASE | SCHEMA} --指定触发器是在DDL事件之前、之后触发。范围是on database、on schema
[WHEN (...)] --可选的WHEN子句,使用逻辑判断来避免触发器无意义的执行
DECLARE --触发器具体内容4-7
Variable declarations
BEGIN
...some code...
END;
Examples:
SQL> CREATE OR REPLACE TRIGGER hr.testtrigger
AFTER CREATE ON SCHEMA -- on schema 作用范围只是在hr用户下create table等触发,其他用户则不会。若是on database则其他用户create table时会触发该触发器
BEGIN
-- 以下使用的是事件属性
DBMS_OUTPUT.PUT_LINE('I believe you have created a ' ||
ORA_DICT_OBJ_TYPE || ' called ' ||
ORA_DICT_OBJ_NAME);
END;
/
Trigger created.
可用事件
可用的DDL事件
DDL事件 | 触发时机 |
ALTER | 对数据库中的任何一个对象使用SQL的ALTER命令时触发 |
ANALYZE | 对数据库中的任何一个对象使用SQL的ANALYZE命令时触发 |
ASSOCIATE STATISTICS | 统计数据关联到数据库对象时触发 |
AUDIT | 通过SQL的AUDIT命令打开审计时触发 |
COMMENT | 对数据库对象做注释时触发 |
CREATE | 通过SQL的CREATE命令创建数据库对象时触发 |
DDL | 列表中所用的事件都会触发 |
DISASSOCIATE STATISTICS | 去掉统计数据和数据库对象的关联时触发 |
DROP | 通过SQL的DROP命令删除数据库对象时触发 |
GRANT | 通过SQL的GRANT命令赋权时触发 |
NOAUDIT | 通过SQL的NOAUDIT关闭审计时触发 |
RENAME | 通过SQL的RENAME命令对对象重命名时触发 |
REVOKE | 通过SQL的REVOKE语句撤销授权时触发 |
TRUNCATE | 通过SQL的TRUNCATE语句截断表时触发 |
可用属性
Oracle 提供了一系列的函数用来提供关于什么触发了DDL触发器以及触发器的状态灯信息。上面那个触发器的例子就使用了属性。
DDL触发器事件以及属性函数
函数名 | 返回值 |
ORA_CLIENT_IP_ADDRESS | 客户端IP地址 |
ORA_DATABASE_NAME | 数据库名称 |
ORA_DES_ENCRYPTED_PASSWORD | 当前用户的DES算法加密后的密码 |
ORA_DICT_OBJ_NAME | 触发DDL的数据库对象名称 |
ORA_DICT_OBJ_NAME_LIST | 受影响的对象数量和名称列表 |
ORA_DICT_OBJ_OWNER | 触发DDL的数据库对象属主 |
ORA_DICT_OBJ_OWNER_LIST | 受影响的对象数量和名称列表 |
ORA_DICT_OBJ_TYPE | 触发DDL的数据库对象类型 |
ORA_GRANTEE | 被授权人数量 |
ORA_INSTANCE_NUM | 数据库实例数量 |
ORA_IS_ALTER_COLUMN | 如果操作的参数column_name指定的列,返回true,否则false |
ORA_IS_CREATING_NESTED_TABLE | 如果正在创建一个嵌套表则返回true,否则false |
ORA_IS_DROP_COLUMN | 如果删除的参数column_name指定的列,返回true,否则false |
ORA_LOGIN_USER | 触发器所在的用户名 |
ORA_PARTITION_POS | SQL命令中可以正确添加分区子句位置 |
ORA_PRIVILEGE_LIST | 授予或者回收的权限的数量。 |
ORA_REVOKEE | 被回收者的数量 |
ORA_SQL_TXT | 触发了触发器的SQL语句的行数。 |
ORA_SYSEVENT | 导致DDL触发器被触发的时间 |
ORA_WITH_GRANT_OPTION | 如果授权带有grant选项,返回true。否则false |
更多属性函数请参考官方文档PL/SQL Language Reference -> Triggers and Oracle Database Data Transfer Utilities
使用事件和属性
Examples:
--创建数据库对象时发出警告,删除数据库对象时阻止
CREATE OR REPLACE TRIGGER HR.no_drop
BEFORE DDL ON DATABASE
BEGIN
IF ORA_SYSEVENT = 'CREATE'
THEN
DBMS_OUTPUT.PUT_LINE('Warning !!! You have created a '||
ORA_DICT_OBJ_TYPE ||' called '||
ORA_DICT_OBJ_NAME|| '; UserName(creater):'||
ORA_DICT_OBJ_OWNER||'; IP:'||
ORA_CLIENT_IP_ADDRESS||'; event:'||
ORA_SYSEVENT);
ELSIF ORA_SYSEVENT = 'DROP'
THEN
RAISE_APPLICATION_ERROR (-20000,
'Cannot create the ' || ORA_DICT_OBJ_TYPE ||
' named ' || ORA_DICT_OBJ_NAME ||
' as requested by ' || ORA_DICT_OBJ_OWNER);
END IF;
END;
--操作了数据库表的哪一列
CREATE OR REPLACE TRIGGER preserve_app_cols
AFTER ALTER ON SCHEMA
DECLARE
-- cursor to get columns in a table
CURSOR curs_get_columns (cp_owner VARCHAR2, cp_table VARCHAR2)
IS
SELECT column_name
FROM all_tab_columns
WHERE owner = cp_owner AND table_name = cp_table;
BEGIN
-- if it was a table that was altered...
IF ora_dict_obj_type = 'TABLE'
THEN
-- for every column in the table...
FOR v_column_rec IN curs_get_columns (
ora_dict_obj_owner,
ora_dict_obj_name
)
LOOP
-- if the current column was the one that was altered then say so
IF ora_is_alter_column (v_column_rec.column_name)
THEN
-- if the table/column is core?
IF is_application_column (
ora_dict_obj_owner,
ora_dict_obj_name,
v_column_rec.column_name
)
THEN
RAISE_APPLICATION_ERROR (
-20001,
'Cannot alter core application attributes'
);
END IF; -- table/column is core
END IF; -- current column was altered
END LOOP; -- every column in the table
END IF; -- table was altered
END;
--属性函数返回值列表
CREATE OR REPLACE TRIGGER hr.what_privs
AFTER GRANT ON SCHEMA
DECLARE
v_grant_type VARCHAR2 (30);
v_num_grantees BINARY_INTEGER;
v_grantee_list ora_name_list_t;
v_num_privs BINARY_INTEGER;
v_priv_list ora_name_list_t;
BEGIN
v_grant_type := ora_dict_obj_type;
v_num_grantees := ora_grantee (v_grantee_list);
v_num_privs := ora_privilege_list (v_priv_list);
IF v_grant_type = 'ROLE PRIVILEGE'
THEN
DBMS_OUTPUT.put_line (
CHR (9) || 'The following roles/privileges were granted'
);
FOR counter IN 1 .. v_num_privs
LOOP
DBMS_OUTPUT.put_line (
CHR (9) || CHR (9) || 'Privilege ' || v_priv_list (counter)
);
END LOOP;
ELSIF v_grant_type = 'OBJECT PRIVILEGE'
THEN
DBMS_OUTPUT.put_line (
CHR (9) || 'The following object privileges were granted'
);
FOR counter IN 1 .. v_num_privs
LOOP
DBMS_OUTPUT.put_line (
CHR (9) || CHR (9) || 'Privilege ' || v_priv_list (counter)
);
END LOOP;
DBMS_OUTPUT.put (CHR (9) || 'On ' || ora_dict_obj_name);
IF ora_with_grant_option
THEN
DBMS_OUTPUT.put_line (' with grant option');
ELSE
DBMS_OUTPUT.put_line ('');
END IF;
ELSIF v_grant_type = 'SYSTEM PRIVILEGE'
THEN
DBMS_OUTPUT.put_line (
CHR (9) || 'The following system privileges were granted'
);
FOR counter IN 1 .. v_num_privs
LOOP
DBMS_OUTPUT.put_line (
CHR (9) || CHR (9) || 'Privilege ' || v_priv_list (counter)
);
END LOOP;
ELSE
DBMS_OUTPUT.put_line ('I have no idea what was granted');
END IF;
FOR counter IN 1 .. v_num_grantees
LOOP
DBMS_OUTPUT.put_line (
CHR (9) || 'Grant Recipient ' || v_grantee_list (counter)
);
END LOOP;
END;
demo:
--创建登录、退出触发器。
CREATE TABLE log_event
(user_name VARCHAR2(10),
address VARCHAR2(20),
logon_date timestamp,
logoff_date timestamp); --创建登录触发器
CREATE OR REPLACE TRIGGER tr_logon
AFTER LOGON ON DATABASE
BEGIN
INSERT INTO log_event (user_name, address, logon_date)
VALUES (ora_login_user, ora_client_ip_address, systimestamp);
END tr_logon;
--创建退出触发器
CREATE OR REPLACE TRIGGER tr_logoff
BEFORE LOGOFF ON DATABASE
BEGIN
INSERT INTO log_event (user_name, address, logoff_date)
VALUES (ora_login_user, ora_client_ip_address, systimestamp);
END tr_logoff;
Oracle 触发器(一)的更多相关文章
- 2014/11/06 Oracle触发器初步 2014-11-06 09:03 49人阅读 评论(0) 收藏
触发器我就不多解释了,保证数据的完整性的神器,嗯..也是减少程序员工作托管给数据库操作的好帮手.就不讲一些大道理了.通俗点,我们对数据库的操作,无非就是增 删 改 查. 触发器就是在删,改,增的时候( ...
- oracle触发器加条件判断
oracle触发器加条件判断,如果某个字段,isnode=0,那么不执行下面的方法,数据如下: create or replace trigger tr_basestation_insert_emp ...
- [转]连续创建多个Oracle触发器失败,单个创建才成功的解决方法
连续创建多个Oracle触发器失败,单个创建才成功的解决方法 1.当我连续执行创建多个触发器时,总是报编译通过,但存在警告或错误.如下: create or replace trigger t ...
- Oracle 触发器在日志管理开发中的应用
摘要: 本文讨论了利用数据库中的触发器对日志管理进行设计与实现的方法, 是对原来在客户端软件中编写日志管理方法的一种改进, 并给出了 Oracle9i 中的实例演示.关键词: Oracle; 触发器; ...
- oracle触发器应用
首先给大家推荐两篇我看后的博文,我已经内容转载过来: 1.对触发器的讲解 本篇主要内容如下: 8.1 触发器类型 8.1.1 DML触发器 8.1.2 替代触发器 8.1.3 系统触发器 8.2 创建 ...
- Mybatis 插入操作时获取主键 (Oracle 触发器与SEQ)
1.通过Oracle序列 -- Create sequence create sequence SEQ_DW_EWSYSTEM minvalue 1 maxvalue 9999999999999999 ...
- SQL server与Oracle触发器的创建与使用
SQL Server 1创建触发器 GO BEGIN IF (object_id('WMY', 'tr') is not null) DROP trigger WMY END; GO CREATE T ...
- oracle触发器中增删改查本表
oracle触发器中增删改查本表 (1)只有before insert触发器中才可以查询或更新本表 create or replace trigger tri_test_ins before inse ...
- Oracle触发器用法实例详解
转自:https://www.jb51.net/article/80804.htm. 本文实例讲述了Oracle触发器用法.分享给大家供大家参考,具体如下: 一.触发器简介 触发器的定义就是说某个条件 ...
- Oracle 触发器(二)
Oracle触发器详解 触发器是许多关系数据库系统都提供的一项技术.在oracle系统里,触发器类似过程和函数,都有声明,执行和异常处理过程的PL/SQL块. 8.1 触发器类型 触发器在数据库里 ...
随机推荐
- 深入理解javascript中的Function.prototye.bind
函数绑定(Function binding)很有可能是你在开始使用JavaScript时最少关注的一点,但是当你意识到你需要一个解决方案来解决如何在另一个函数中保持this上下文的时候,你真正需要的其 ...
- NodeJS require路径
项目需要用nodejs,感觉nodejs是前端装逼神器了,是通向全栈工程师的必经之路哇,接下来开始踏上学习nodejs的征程.下面是第一个hello,world的程序. 1.server.js文件,这 ...
- Bzoj1496: [NOI2006]千年虫
题面 传送门 Sol 左右可以分开搞 然后就是要形成一个类似梳子的东西 设\(f[0/1][i][j]\) \(0\)凹,\(1\)凸,\(i\)为行,可以滚一维,\(j\)为该行长度 \(f[0][ ...
- mui.ajax()和asp.net sql服务器数据交互【2】json数组和封装
今天没有做循环创建显示:可以参考张鑫旭的文章:<基于HTML模板和JSON数据的JavaScript交互> 1.ashx页面代码 //下面的封装一般框架底层都是写好的:连接 数据库和获取D ...
- Django—自定义分页
分页功能在每个网站都是必要的,对于分页来说,其实就是根据用户的输入计算出应该显示在页面上的数据在数据库表中的起始位置. 确定分页需求: 1. 每页显示的数据条数 2. 每页显示页号链接数 3. 上一页 ...
- Python基础-继承与派生
一.继承 继承是一种创建新的类的方式,在python中,新建的类可以继承自一个或者多个父类,原始类称为基类或超类,新建的类称为派生类或子类. python中类的继承分为:单继承和多继承 class P ...
- 花1台的钱入手2台【最能抗DDoS】阿里云主机【攻略】
花1台的钱入手2台[最能抗DDoS]阿里云主机[攻略]: 第一步:先申请0元半年 http://click.aliyun.com/m/335/:注:0元机器只有新帐号可申请第二步:再买6折37/月 h ...
- restful课程凌杂知识点
request.post:字典形式数据 request.body:收到的是源数据
- salt 之 master and minion
系统:centos7.2 master:192.168.1.41minion:192.168.1.46 注释: setenforce 0 --关闭selinux systemctl stop fire ...
- cookie的初识和运用(js和jq)
cookie是什么 cookie是浏览器提供的一种机制,它将document 对象的cookie属性提供给JavaScript.可以由JavaScript对其进行控制,而并不是JavaScript本身 ...