挂起语句, 是指数据库 Hang 到那不能动了, 触发的.

1. DML 触发器

这种类型的触发器对于开发人员都很常见, 其他类型的触发器主要是给DBA使用的.

配置触发器,我们需要回答以下问题:

  • 触发器应该是对整个DML语句触发一次, 还是要为语句涉及的每一行都触发一次 ?
  • 触发器是应该在整个语句开始之前或者结束之后, 或者是在对每一行记录处理之前或者之后触发 ?
  • 触发器到底是由插入, 更新, 删除或者是某种组合触发的 ? (oracle 11g 开始支持多个操作组合触发器)

如果我在 books 表上定义了一个行级的更新触发器, 这个触发器就会被触发 1000 次.

提示:如果我们把 DML 触发器定义成自治事务, 则在触发器中执行的所有DML语句都可以保存或者回滚-使用显示的 commit 或者 rollabck 语句- 不会影响到主事务.

千万不要这么做, 记住, 自治事务不是一个好东西, 只有在两个地方可以使用它.

1. 你想演示某种现象, 用来演示的, 没有实际意义.

2. 你想记录下所有的修改, 即便是没有成功提交的修改, 你也想知道, 这时单独使用一个自治事务, 自治事务内部记录修改的动作, 然后自治事务单独commit.

 -- chap19_01.sql
create or replace trigger validate_employee_changes
after
insert or update on employees
for each row
begin
check_date(:NEW.hire_date);
check_email(:NEW.email);
end;
/
show errors; -- chap19_02.sql
create or replace trigger show_insert_ttt
after
insert on ttt
for each row
begin
dbms_output.put_line('You just now insert some rows');
end;
/ -- chap19_03.sql
create or replace trigger bef_ins_ceo_comp
before
insert on ceo_compensation
for each row
declare
pragma autonomous_transaction; -- 自治事务的意思
begin
insert into ceo_comp_history
values(:NEW.name, :OLD.compensation,
:NEW.compensation, 'After insert', SYSDATE);
commit; -- 自治事务可以 commit;
end;
/
show errors; -- chap19_04.sql
create or replace trigger check_raise
after
update of salary on employees
for each row
when ((OLD.salary != NEW.salary) OR (OLD.salary IS NULL AND NEW.salary IS NOT NULL) OR
(OLD.salary is not null and NEW.salary is null))
begin
-- ...
end;
/
show errors;

注意下边, 只是在有 WHEN 子句时, 有关WHEN 子句需要注意的内容,

NEW 和 OLD 之所以说是伪记录, 是因为它们不具有真正的PL/SQL记录应该有的属性, OLD保留的是要处理记录的原始值, NEW代表的是新的值. 这些记录和使用该表的%rowtype属性声明的记录结构完全相同.

可以使用 referencing 子句来修改 new 和 old 的名字, 但是个人感觉用处不大.

 create or replace trigger check_raise
after
update of salary on employees
for each row
when ((OLD.salary != NEW.salary) OR (OLD.salary IS NULL AND NEW.salary IS NOT NULL) OR
(OLD.salary is not null and NEW.salary is null))
begin
-- ...
end;
/
show errors; -- chap19_05.sql
create or replace trigger audit_update
after
update on frame
referencing old as prior_to_cheat NEW as after_cheat -- 修改old 和 new 的名字, 但是个人认为用处不大
for each row
begin
insert into frame_audit ( bowler_id, game_id, old_score,
new_score, change_date, operation)
values (:after_cheat.bowler_id, :after_cheat.game_id, :prior_to_cheat.score,
:after_cheat.score, SYSDATE, 'UPDATE');
end;
/
show errors;

 create or replace trigger three_for_the_price_of_one
before
delete or insert or update on account_transaction
for each row
begin
-- 判断insert
if inserting then
:new.created_by := user;
:new.created_date := sysdate;
elsif deleting then
audit_deletion(user, sysdate);
elsif updating then
:new.updated_by := user;
:new.updated_date := sysdate;
end if;
end;
/
show errors;

另外, 针对 updating 还有专门确认是不是专门针对某一列的更改, updating('column'), 用这个判断, 即便是修改操作, 但是不是修改这列也不会触发相应操作.

触发器大例子

Trustful 夫人经营着一家保龄球馆, 一直接到投诉说有人对比分做手脚, 最近她安装了一个计分系统, 想通过这个系统抓住那些作弊者.

 create table frame(
bowler_id number,
game_id number,
frame_number number,
strike varchar2(1) default 'N',
spare varchar2(1) default 'N',
constraint frame_pk
primary key (bowler_id, game_id, frame_number)
); create table frame_audit(
bowler_id number,
game_id number,
frame_number number,
old_strike varchar2(1),
new_strike varchar2(1),
old_spare varchar2(1),
new_spare varchar2(1),
old_score number,
new_score number,
change_date date,
operation varchar2(6)
);
 create or replace trigger
after
insert or update or delete on frame
for each row
begin
if inserting then
insert into frame_audit(bowler_id, game_id, frame_number,
new_strike, new_spare, new_score,
change_date, operation)
values (:NEW.bowler_id, :NEW.game_id, :NEW.frame_number,
:NEW.strike, :NEW.spare, :NEW.score,
SYSDATE, 'INSERT');
elsif updating then
insert into frame_audit(bowler_id, game_id, frame_number,
old_strike, new_strike,
old_spare, new_spare,
old_score, new_score,
change_date, operation)
values (:NEW.bowler_id, :NEW.game_id, :NEW.frame_number,
:OLD.strike, :NEW.strike,
:OLD.spare, :NEW.spare,
:OLD.score, :NEW.score,
SYSDATE, 'UPDATE');
elsif deleting then
insert into frame_audit(bowler_id, game_id, frame_number,
old_strike, old_spare, old_score,
change_date, operation)
values (:OLD.bowler_id, :OLD.game_id, :OLD.frame_number,
:OLD.strike, :OLD.spare, :OLD.score,
SYSDATE, 'DELETE');
end if;
end audit_frames;
/
show errors;

上例中的 update 可以替换的更精确一些, 例如 :

 create or replace trigger audit_update
after
update of strike, spare, score of frame -- 具体指定了有哪些列
for each row
when (old.strike != new.strike or
old.spare != new.spare or
old.score != new.score)
begin
-- ...
end;
/
show errors;

多个触发器执行的顺序, 从 oracle 11g 开始可以通过 follows 子句来保证. 例如:

 create or replace trigger increment_by_two
before
insert on incremented_values
for each row
follows incretment_by_one -- 另一个触发器的名字
begin
if :new.value_increamentd > 1 then
:new.value_incrementd := :new.value_incremented + 2;
end if;
end;

如果触发器的内容是修改了本表(即触发该触发器的表), 那么可能存在问题, 所以给出一些指导:

  • 通常来说, 行级触发器不应该去读或者写导致它被触发的表的内容, 不过这个约束只针对行级触发器, 语句级触发器可以随意读写其触发表的内容, 这也是避免"突变表"错误的一种方法.
  • 如果我们的触发器使用自治事务(通过使用 PRAGMA AUTONOMOUS TRANSACTION语句, 以及在触发器体内执行了提交), 我们就可以查询触发表的内容, 不过我们仍然不能修改表的内容.

下边的内容, 复合触发器, 是 oracle 11g 以后才支持的.

个人感觉复合触发器不好, 暂时忽略吧. 还是分开写好

 create or replace trigger town_cirer
after
create on schema -- 创建所有的对象都会触发, 不仅仅是 table
begin
dbms_ouput.put_line('I believe you have created something!');
end;
/
show errors;

可以提供一些属性, 来方便查看创建了那些对象等等.

使用事件和属性

 create or replace trigger no_create
after
create on schema
begin
raise_application_error( -20000, 'Error: Objects cannot be created in the production database.');
end;
/
show errors;

上例执行以后, 所有的创建对象都不可以执行.(要知道触发器是事务的一部分)
利用属性:

 create or replace trigger no_create
after
create on schema
begin
raise_application_error(-20000,
'Cannot create the' || ORA_DICT_OBJ_TYPE ||
'named ' || ORA_DICT_OBJ_NAME ||
'as requested by' || ORA_DICT_OBJ_OWNER ||
' in production.');
end;
/
show errors;
 create or replace trigger no_create
before
ddl on schema
begin
if ora_sysevent = 'CREATE' then
raise_application_error(-20000,
'Cannot create the' || ORA_DICT_OBJ_TYPE ||
'named ' || ORA_DICT_OBJ_NAME ||
'as requested by' || ORA_DICT_OBJ_OWNER ||
' in production.');
elsif ora_sysevent = 'DROP' then
-- ...
end if;
end;
/
show errors;

删除不可删除的, 特殊处理

比如你创建了一个不能删除一些对象的触发器, 那么完蛋了, 以后你再也别想删除任何对象了, 因为这个限制删除的触发器本身就是个对象, 但是 oracle 提供的是, 你可以删除这个触发器对象

 create or replace trigger undroppable
before
drop on schema
begin
raise_application_error(-20000, 'You cannot drop me! I am invincible!');
end;
/
show errors; -- 上边的不能删除任何对象的触发器创建好了, 你不可以删除一般的对象了, 但是
drop trigger undroppable; -- 这条语句是可以顺利执行的

这里要自己想一下, 例如没有 before startup 触发器, 这是肯定的, 还没startup 触发器怎么运行, 同样, 没有 after shutdown 触发器

没有 before logon 触发器(这个含义是, 等着, 某人要来, 你怎么知道某人要来, 所以语义不通), 没有 after logoff 没有 before servererror 触发器

再例如 servererror 触发器

 create or replace trigger error_log
after
servererror on database
begin
central_error_log.log_error; -- 调用包
end;

INSTEAD OF 触发器

主要的需求是, 无法直接对视图进行插入, 删除等操作.

例子:

   1:  create or replace trigger error_log
   2:      after
   3:      servererror on database
   4:  begin
   5:      central_error_log.log_error;  -- 调用包
   6:  end;
   7:   
   8:  -- chap19_16.sql
   9:  create table delivery(
  10:      delivery_id    number,
  11:      delivery_start date,
  12:      delivery_end    date,
  13:      area_id        number,
  14:      driver_id    number
  15:  );
  16:   
  17:  create table area(
  18:      area_id    number,
  19:      area_desc    varchar2(30)
  20:      );
  21:      
  22:  create table driver(
  23:      driver_id    number,
  24:      driver_name    varchar2(30)
  25:      );
  26:      
  27:  create sequence delivery_id_seq;
  28:  create sequence area_id_seq;
  29:  create sequence driver_id_seq;
  30:   
  31:  create or replace view delivery_info 
  32:  as
  33:  select d.delivery_id,
  34:          d.delivery_start,
  35:          d.delivery_end,
  36:          a.area_desc,
  37:          dr.driver_name
  38:    from delivery d,
  39:          area a,
  40:          driver dr
  41:   where a.area_id = d.area_id
  42:     and dr.driver_id = d.driver_id

   1:  create or replace trigger delivery_info_insert
   2:      instead of insert
   3:      on delivery_info
   4:  declare
   5:      cursor curs_get_driver_id(cp_driver_name varchar2)
   6:      is
   7:          select driver_id
   8:            from driver
   9:           where driver_name = cp_driver_name;
  10:      v_driver_id    number;
  11:      
  12:  begin
  13:      -- ...
  14:  end;
  15:  /

AFTER SUSPEND 触发器 (挂起触发器)

另外还提供了很多函数, 关于挂起的内容, 个人感觉, 这个地方可以忽略

管理触发器

alter trigger trigger_name disable;

alter trigger trigger_name enable;

drop trigger trigger_name;

查看触发器

DBA_TRIGGERS

ALL_TRIGGERS

USER_TRIGGERS

检查触发器的有效性

plsql programming 19 触发器的更多相关文章

  1. plsql programming 11 记录类型

    记录类型非常类似数据库表中的行. 记录作为一个整体本身并没有值, 不过每个单独成员或字段都有值, 记录提供了一种把这些值当做一组进行操作的方法. 例如: 1: -- create a table 2: ...

  2. plsql programming 17 过程, 函数与参数

    代码模块化, 即将一大块代码拆成若干小块(过程), 然后就可以在其他模块调用这些模块了, 这样, 重用性更好, 也方便管理. 过程: 过程是一个可以像执行 PL/SQL 语句一样调用的程序, 一个过程 ...

  3. Oracle 学习笔记 19 -- 触发器和包浅析(PL/SQL)

    触发器是存放在数据库中的一种特殊类型的子程序.不能被用户直接调用,而是当特定事件或操作发生时由系统自己主动 调用执行.触发器不能接受參数.所以执行触发器就叫做触发或点火.Oracle事件指的是数据库的 ...

  4. plsql programming 20 管理PL/SQL代码(个人感觉用不到)

    这一章的内容, 只完成了一部分, 剩下的用到再补充吧 由于依赖关系, 而编译失败, 需要重新编译. ( 所谓依赖, 是指存储过程, 函数等在运行中调用的对象, 比如table 等, 比如你删除了过程中 ...

  5. plsql programming 14 DML和事务管理

    我们可以把多个SQL语句集中在一起, 在逻辑上组成一个事务, 从而保证这些操作或者全部被保存到数据库(用sql的说法就是”提交”), 或者被整体驳回(用sql的说法是“回滚”). 事务: ACID 原 ...

  6. plsql programming 13 其他数据类型

    bolean 类型 raw 类型, 用来保存和操作少量的二进制数据. urowid 和 rowid 类型, 这两种数据类型表示数据库的 rowid. 所谓 rowid 就是一个标识符-用来表示数据库中 ...

  7. plsql programming 16 动态SQL和动态PLSQL

    动态SQL 是指在执行时才构建 SQL 语句, 相对于静态 sql 的编译时就已经构建. 动态PLSQL 是指整个PL/SQL代码块都是动态构建, 然后再编译执行的. 作用: 1. 可以支持 DDL ...

  8. plsql programming 04 条件和顺序控制

    1. 条件语句 if salary > 40000 or salary is NULL then give_bonus(employee_id, 500); end if; if conditi ...

  9. mysql 开发基础系列19 触发器

    触发器是与表有关的数据库对象,触发器只能是针对创建的永久表,而不能是临时表. 1.1 创建触发器 -- 语法: CREATE TRIGGER trigger_name trigger_time tri ...

随机推荐

  1. xcodebuild和xcrun实现自动打包iOS应用程序

    随着苹果手持设备用户的不断增加,ios应用也增长迅速,同时随着iphone被越狱越来越多的app 的渠道也不断增多,为各个渠道打包成了一件费时费力的工作,本文提供一种比较智能的打包方式来减少其带来的各 ...

  2. [工作积累] GCC 4.6 new[] operator内存对齐的BUG

    对于用户没有定义dctor(包括其所有成员)的类来说, new CLASS[n] 可能会直接请求sizeof(CLASS)*n的空间. 而带有dctor的 类, 因为delete[]的时候要逐个调用析 ...

  3. tcp 多线程与多进程调用close

    http://blog.csdn.net/russell_tao/article/details/13092727 大家知道,所谓线程其实就是“轻量级”的进程.创建进程只能是一个进程(父进程)创建另一 ...

  4. FormCreate & FormActivate & FormShow执行顺序演示

    procedure TForm1.FormCreate(Sender: TObject);begin  form1.Caption:=form1.Caption +'+Create'; end; pr ...

  5. jQuery新的事件绑定机制on()

    浏览jQuery的deprecated列表,发现live()和die()在里面了,赶紧看了一下,发现从jQuery1.7开始,jQuery引入了全新的事件绑定机制,on()和off()两个函数统一处理 ...

  6. display:inline-block 在IE6中实现{转}

    IE6/IE7下对display:inline-block的支持性不好. 1.inline元素的display属性设置为inline-block时,所有的浏览器都支持: 2.block元素的displ ...

  7. BZOJ2435: [Noi2011]道路修建

    这种水题真是……没一次AC都不好意思见人啊 P.S. LINUX无限栈真是爽炸了… 我爱递归 /**************************************************** ...

  8. ZOJ3720 Magnet Darts(点在多边形内)

    第一道点在多边形内的判断题,一开始以为是凸的.其实题意很简单的啦,最后转化为判断一个点是否在一个多边形内. 如果只是简单的凸多边形的话,我们可以枚举每条边算下叉积就可以知道某个点是不是在范围内了.但对 ...

  9. robotframework 测试工具添加PDF文件内容匹配插件

    robotframework  这个需要了解的请度娘.本文实现的是一个小功能.大体分为如下几个步骤 1)给定一个pdf文件. 2)读取pdf文件内容,并解析为文本内容. 3)通过给定的内容,比对pdf ...

  10. POJ 2109

    #include<iostream> #include<stdio.h> #include<math.h> using namespace std; int mai ...