Oracle触发器5(INSTEAD OF触发器)

前提:对于简单的视图,可以直接进行DML操作,但是对于复杂视图,不允许直接执行DML操作,当视图符合以下任何一种情况都不可以:

具有集合操作符(UNION,UNION ALL,INTERSECT,MINUS);
    具有分组函数(MIN,MAX,SUM,AVG,COUNT);
    具有GROUP BY,CONNECT BY 或START WITH子句;
    具有DISTINCT关键字
    具有连接查询

在具有以上情况的复杂视图执行DML操作,必须要基于视图建立INSTEAD OF触发器。

建立之后,就可以基于复杂视图执行DML语句

注意事项:

INSTEAD OF选项只适用于视图
    当基于视图建立触发器时,不能指定BEFORE和AFTER选项
    在建立视图时没有指定WITH CHECK OPTION选项
    当建立INSTEAD OF触发器时,必须指定FOR ECH ROW选项

——又小又大型的例子:

1.建立复杂视图dept_emp

视图时逻辑表,本身没有任何数据。

视图只是对应一条SELECT语句。

当查询视图时,其数据实际是从视图基表上取得。

为了简化部门及其雇员信息的查询,应建立复杂视图dept_emp

CREATE OR REPLACE VIEW dept_emp AS

SELECT a.deptno,a.dname,b.empno,b.ename

FROM dept a,emp b

WHERE a.deptno=b.deptno;

当执行以上语句之后,直接查询视图会显示相关信息,但不允许DML操作。

2.建立INSTEAD OF触发器

为了可以在复杂视图上执行DML操作,必须要基于复杂视图来建立INSTEAD OF触发器。

下面以复杂视图dept_emp上执行INSERT 操作为例:

CREATE OR REPLACE TRIGGER tr_instead_of_dept_emp

INSTEAD OF INSERT ON dept_emp

FOR EACH ROW

DELARE

v_temp INT;

BEGIN

SELECT count(*) INTO v_temp FROM dept

WHERE deptno=:new.deptno;

IF v_temp=0 THEN

INSERT INTO dept(deptno,dname) VALUES(:new.deptno,:new.dname);

END IF;

SELECT count(*) INTO v_temp FROM emp

WHERE empno=:new.empno;

IF v_temp=0 THEN


分类: ORACLE 2007-09-27 21:56 2455人阅读 评论(6) 收藏 举报

今天在论坛上遇到个触发器的问题,需求如下:

向一个表中插入一条新记录 如(2007 ,a)
触发器  查询该表中如果有(2007 ,a)这条数据就更新这条数据中的另一个字段  如果没有就插入一条新记录触发器
  if(如果存在)
    更新记录
  else
    插入新记录

由于本人也不经常写复杂的触发器,经过一番尝试终于得出结论.如果想要中止触发动作在oracle里面似乎只能抛出异常,否则是无法实现的,只有使用Instead of 才行,于是建立视图

create or replace view vw_ayear as
select "YR","STARTFLAG","STARTDATE","CLOSETAG","TURNFLAG" from ayear
然后在写触发器

CREATE OR REPLACE TRIGGER ayear_TR
INSTEAD OF INSERT
ON vw_ayear
FOR EACH ROW
DECLARE TEMP INT ;
 BEGIN
  select 0 into TEMP from dual;
  select count(*) into TEMP from ayear a where a.yr=:new.yr and a.startflag=:new.startflag;
  IF TEMP <> 0 THEN
   UPDATE ayear a SET a.startdate = :new.startdate , a.closetag=:new.closetag , a.turnflag=:new.turnflag
     where a.yr=:new.yr and a.startflag=:new.startflag;
 ELSE
     insert into ayear values(:new.yr,:new.startflag,:new.startdate,:new.closetag,:new.turnflag);
 END IF;
 END;
 
最后测试

insert into vw_ayear values('2008','1',to_date('2007-04-01','yyyy-MM-dd'),'0','0')

触发器里面不能有commit,rollback的操作  
  无法操作触发器所在的这个表,不管是before 还是after
  唯一的方法就是在不满足条件时人工抛出一个错误

还有就是使用视图了,呵呵,不知道说的对不对如果错了还请高人赐教.


Oracle Instead of 触发器的使用

2009-05-21 17:31:37|  分类: oracle |字号 订阅

 
有两张表的一个字段需要进行同步更新,也就是A表修改时要把对应的B表的记录
字段修改,反过来B表修改时也要把A表的修改,保持两边数据的一个同步,这个可以在前台很容易的实现,但开发
人员不想修改代码了,就考虑在后台用trigger实现。
      功能很简单,但在实现时遇到一个问题,就是A上的DML触发了上面的TRIGGER,然后这个TRIGGER去更新B表,这样
就会触发B表上的触发器,而B表上的TRIGGER又会更新A表,这样就迭代触发,没有结束了,也就是会产生变异表(mutating)
我不知道ORACLE的触发器是否有属性来限制这种情况的发生,但以前做SQL SERVER时知道有种Instaed of的触发器,他表示
当DML启动他后,他将以TRIGGER里的代码来代替这个DML动作,也就是DML不会真正的执行,只会启动INSTEAD TRIGGER,最终
执行的是TRIGGER里面的编码。
      查看了Docs,看到ORACLE也支持这个类型的触发器,但这个只能建立到视图上,不能基于表建立,我要的功能肯定是可以
实现的,在这里我把原表进行了rename,引如了两张视图,名字就是以前的表名,这样对于他们前台应用就做了个
透明的切换,然后在两个视图上建立INSTEAD触发器,将任何两个视图上的更新都传播到后面的两个基表,这样不管你更新那个
视图,我都可以捕获到数据,以代码在后面更新,也不存在互相触发,因为触发器修改的对象已经转移到表了,而此时表上是没有
trigger的,呵呵!!!
过程如下

--创建测试表
SQL> create table mytest1(row_num number,row_name varchar2(50));

表被创建

SQL> create table mytest2(row_num number,row_name varchar2(50));

表被创建

--测试数据
SQL> INSERT INTO MYTEST1 VALUES(1,'Fuck!!!');

1 行 已插入

SQL> INSERT INTO MYTEST2 VALUES(1,'Watch your mouth!!!');

1 行 已插入

SQL> COMMIT;

提交完成

--先在一个表上创建触发器
SQL> CREATE OR REPLACE TRIGGER TRI_TEST1
  2  BEFORE UPDATE
  3  ON MYTEST1
  4  FOR EACH ROW
  5  DECLARE
  6  lv_new VARCHAR2(20);
  7  lv_parent VARCHAR2(20);
  8  BEGIN
  9      lv_new := :new.row_name;
 10      lv_parent := :OLD.row_name;
 11      IF lv_new <> lv_parent THEN
 12          UPDATE MYTEST2
 13          SET ROW_NAME = :NEW.ROW_NAME
 14          WHERE ROW_NUM = :NEW.ROW_NUM;
 15      END IF;
 16      DBMS_OUTPUT.PUT_LINE(lv_new || lv_parent);
 17  END;
 18  /

触发器被创建

--测试更新
SQL> set serveroutput on
SQL> UPDATE MYTEST1 SET ROW_NAME = 'DO it!!!';
DO it!!! Fuck!!!

1 行 已更新

--更新成功
SQL> SELECT * FROM MYTEST2;

ROW_NUM ROW_NAME
---------- --------------------------------------------------
         1 DO it!!!
        
--另外张表创建触发器
SQL> CREATE OR REPLACE TRIGGER TRI_TEST2             
  2  BEFORE UPDATE                                   
  3  ON MYTEST2                                      
  4  FOR EACH ROW                                    
  5  DECLARE                                         
  6  lv_new VARCHAR2(20);                            
  7  lv_parent VARCHAR2(20);                         
  8  BEGIN                                           
  9      lv_new := :new.row_name;                       
 10      lv_parent := :OLD.row_name;                    
 11      IF lv_new <> lv_parent THEN                     
 12          UPDATE MYTEST1                               
 13          SET ROW_NAME = :NEW.ROW_NAME                 
 14          WHERE ROW_NUM = :NEW.ROW_NUM;                
 15      END IF;                                        
 16      DBMS_OUTPUT.PUT_LINE(lv_new || lv_parent);     
 17  END;                                            
 18  /  
 
--产生了变异表,更新失败
SQL> update mytest1 set row_name = 'mouthkkkkkoo';

update mytest1 set row_name = 'mouthkkkkkoo'

ORA-04091: table MYTEST1 is mutating, trigger/function may not see it
ORA-06512: at "TRI_TEST2", line 8
ORA-04088: error during execution of trigger 'TRI_TEST2'
ORA-06512: at "TRI_TEST1", line 8
ORA-04088: error during execution of trigger 'TRI_TEST1'

--更新失败
SQL> update mytest2 set row_name = 'mouthkkkkkoo';

update mytest2 set row_name = 'mouthkkkkkoo'

ORA-04091: table MYTEST2 is mutating, trigger/function may not see it
ORA-06512: at "TRI_TEST1", line 8
ORA-04088: error during execution of trigger 'TRI_TEST1'
ORA-06512: at "TRI_TEST2", line 8
ORA-04088: error during execution of trigger 'TRI_TEST2'

--删除触发器
SQL> drop trigger TRI_TEST2;

触发器被删掉

SQL> drop trigger TRI_TEST1;

触发器被删掉

--创建视图
SQL> CREATE VIEW V_TEST1 AS SELECT * FROM MYTEST1;

视图被创建

SQL> CREATE VIEW V_TEST2 AS SELECT * FROM MYTEST2;

视图被创建

--基于视图创建Instead触发器
SQL> CREATE OR REPLACE TRIGGER TRI_TEST1
  2  INSTEAD OF UPDATE
  3  ON V_TEST1
  4  FOR EACH ROW
  5  DECLARE
  6  lv_new VARCHAR2(20);
  7  lv_parent VARCHAR2(20);
  8  BEGIN
  9      lv_new := :new.row_name;
 10      lv_parent := :OLD.row_name;
 11      IF lv_new <> lv_parent THEN
 12          UPDATE MYTEST2
 13          SET ROW_NAME = :NEW.ROW_NAME
 14          WHERE ROW_NUM = :NEW.ROW_NUM;
 15          UPDATE MYTEST1
 16          SET ROW_NAME = :NEW.ROW_NAME
 17          WHERE ROW_NUM = :NEW.ROW_NUM;
 18      END IF;
 19      DBMS_OUTPUT.PUT_LINE(lv_new || lv_parent);
 20  END;
 21  /

触发器被创建

SQL> CREATE OR REPLACE TRIGGER TRI_TEST2
  2  INSTEAD OF UPDATE
  3  ON V_TEST2
  4  FOR EACH ROW
  5  DECLARE
  6  lv_new VARCHAR2(20);
  7  lv_parent VARCHAR2(20);
  8  BEGIN
  9      lv_new := :new.row_name;
 10      lv_parent := :OLD.row_name;
 11      IF lv_new <> lv_parent THEN
 12          UPDATE MYTEST2
 13          SET ROW_NAME = :NEW.ROW_NAME
 14          WHERE ROW_NUM = :NEW.ROW_NUM;
 15          UPDATE MYTEST1
 16          SET ROW_NAME = :NEW.ROW_NAME
 17          WHERE ROW_NUM = :NEW.ROW_NUM;
 18      END IF;
 19      DBMS_OUTPUT.PUT_LINE(lv_new || lv_parent);
 20  END;
 21  /

触发器被创建

--功能已经实现
SQL> update v_test1 set row_name = 'I rock with you!!!';                                        
                                                            
1 行 已更新                                                 
                                                            
SQL> commit;                                                
                                                            
提交完成                                                    
                                                            
SQL> select * from v_test2;                                 
                                                            
   ROW_NUM ROW_NAME                                         
---------- --------------------------------------------------
         1 I rock with you!!!                                                                    
                                                            
                                                            
SQL> update v_test2 set row_name = 'Don''t kick me!!!';     
                                                            
1 行 已更新                                                 
                                                            
SQL> commit;                                                
                                                            
提交完成                                                    
                                                            
SQL> select * from v_test1;                                 
                                                            
   ROW_NUM ROW_NAME                                         
---------- --------------------------------------------------
         1 Don't kick me!!!                                 
                                                            
SQL>


INSTEAD OF触发器的更多相关文章

  1. pt-online-schema-change中update触发器的bug

    pt-online-schema-change在对表进行表结构变更时,会创建三个触发器. 如下文测试案例中的t2表,表结构如下: mysql> show create table t2\G . ...

  2. MySQL主从环境下存储过程,函数,触发器,事件的复制情况

    下面,主要是验证在MySQL主从复制环境下,存储过程,函数,触发器,事件的复制情况,这些确实会让人混淆. 首先,创建一张测试表 mysql),age int); Query OK, rows affe ...

  3. MySQL 系列(三)你不知道的 视图、触发器、存储过程、函数、事务、索引、语句

    第一篇:MySQL 系列(一) 生产标准线上环境安装配置案例及棘手问题解决 第二篇:MySQL 系列(二) 你不知道的数据库操作 第三篇:MySQL 系列(三)你不知道的 视图.触发器.存储过程.函数 ...

  4. MSSQL 事务,视图,索引,存储过程,触发器

    事务 事务是一种机制.是一种操作序列,它包含了一组数据库操作命令,这组命令要么全部执行,要么全部不执行. 在数据库系统上执行并发操作时事务是作为最小的控制单元来使用的.这特别适用于多用户同时操作的数据 ...

  5. Mysql - 触发器/视图

    触发器在之前的项目中, 应用的着实不多, 没有办法的时候, 才会去用这个. 因为这个东西在后期并不怎么好维护, 也容易造成紊乱. 我最近的项目中, 由于数据库设计(别人设计的)原因, 导致一些最简单功 ...

  6. Oracle使用触发器和mysql中使用触发器的比较——学习笔记

    一.触发器 1.触发器在数据库里以独立的对象存储, 2.触发器不需要调用,它由一个事件来触发运行 3.触发器不能接收参数 --触发器的应用 举个例子:校内网.开心网.facebook,当你发一个日志, ...

  7. 我的MYSQL学习心得(十二) 触发器

    我的MYSQL学习心得(十二) 触发器 我的MYSQL学习心得(一) 简单语法 我的MYSQL学习心得(二) 数据类型宽度 我的MYSQL学习心得(三) 查看字段长度 我的MYSQL学习心得(四) 数 ...

  8. Oracle数据库自动备份SQL文本:Procedure存储过程,View视图,Function函数,Trigger触发器,Sequence序列号等

    功能:备份存储过程,视图,函数触发器,Sequence序列号等准备工作:--1.创建文件夹 :'E:/OracleBackUp/ProcBack';--文本存放的路径--2.执行:create or ...

  9. MySQL触发器-条件触发器语法

    文章为作者原创,未经许可,禁止转载.    -Sun Yat-sen University 冯兴伟 实验4 触发器 )实验目的 掌握数据库触发器的设计和使用方法 )实验内容和要求 定义BEFORE触发 ...

  10. MySQL笔记---视图,存储过程, 触发器的使用入门

    大二学数据库的时候,只是隐约听到老师提起过视图啊,存储过程啊,触发器啊什么的,但只是淡淡的记住了名字,后来自己做些小项目,小程序,也没有用上过,都只是简单的建表,关联表之类的,导致我对这些东西的理解只 ...

随机推荐

  1. Maven: NoClassDefFoundError: org/codehaus/plexus/classworlds/launcher/Launcher

    为了和团队开发环境保持一致,须要 在Ubuntu上安装maven2.2.1,引文我之前已经用apt-get命令安装了3.3的maven.在运行maven命令时报错: Maven: NoClassDef ...

  2. Node.js可以做些什么?

    就像 JavaScript 至client天生,Node.js 生于网络.Node.js 我们可以做更多的不是开发一个网络 站这么简单,采用 Node.js.您可以轻松地开发:  具有复杂逻辑的站点 ...

  3. JavaEE(17) - JPA查询API和JPQL

    1. 获取查询结果 2. JPQL函数和JPQL表达式 #1. 使用from子句 #2. 查询部分属性 #3. 查询中使用构造器 3. JPQL的关联查询和多态查询 #1. 多态查询 #2. 隐式连接 ...

  4. don't touch your phone in any unfamiliar way(转)

    直到这两天 Chainfire 说,他正尝试在不修改手机 /System 分区的情况下获取 Android 系统 Root 权限,我才想起他在今年 9 月轻描淡写地提到自己已经把 SuperSU 卖给 ...

  5. easyUI的combobox实现级联

    先简介下combobox: easyUI重写了select,取而代之的是combobox,有例如以下几种方式能够创建一个combobox 1.使用select标签,并加上class="eas ...

  6. ASP.NET MVC Boilerplate简介

    ASP.NET MVC Boilerplate简介 ASP.NET MVC Boilerplate是专业的ASP.NET MVC模版用来创建安全.快速.强壮和适应性强的Web应用或站点.它在微软默认M ...

  7. Java串口通信详细解释

    前言 说到开源.恐怕非常少有人不挑大指称赞. 学生通过开源码学到了知识,程序猿通过开源类库获得了别人的成功经验及可以按时完毕手头的project,商家通过开源软件赚到了钱……,总之是皆大欢喜. 然而开 ...

  8. CSS hack 如何区分所有IE浏览器和非IE浏览器

    网上方法很多,例如,测试后得出以下结论,多余的话不说了,直入主题: 1.所有的推理IE浏览器 正解:此写法仅仅被lE浏览器识别,非IE浏览器不识别. <!--[if IE]> <st ...

  9. Flex 日志管理

    在Flex中调试方法有两种: 一是用trace()函数,在flex builder中进行调试: 二是用logTarget类,例如以下代码: // Create a target. var logTar ...

  10. NET MVC中使用FluentValidation

    ASP.NET MVC中使用FluentValidation验证实体   1.FluentValidation介绍 FluentValidation是与ASP.NET DataAnnotataion ...