码迷,mamicode.com
首页 > 数据库 > 详细

plsql触发器

时间:2015-02-25 23:56:52      阅读:300      评论:0      收藏:0      [点我收藏+]

标签:replace   commit   before   insert   create   

11 数据库的触发器trigger

定义:根据发生的事件而执行的一种存储子程序

数据库事件:DML事件、DDL事件、用户事件、系统事件
触发器分类:DML触发器、DDL触发器、系统用户事件触发器

11.1 触发器的创建

(1) DML触发器

 A insert事件

create or replace trigger ai_org_trig
  before insert on org_tab
  for each row
begin
  update sec_hrc_audit set num_rows=num_rows+1 where hrc_code=:NEW.hrc_code;
  if sql%notfound then
    insert into sec_hrc_audit values(:NEW.hrc_code,1);
  end if;
exception when others then
  null;
end;

测试:
SQL> insert into org_tab values(4,1008,‘test1‘,‘test2‘);

1 row created.

SQL> commit;

Commit complete.

SQL> select * from sec_hrc_audit;

  HRC_CODE   NUM_ROWS
---------- ----------
     4        1
     1        3
     2        3

SQL> insert into org_tab values(4,1009,‘test1‘,‘test2‘);

1 row created.

SQL> commit;

Commit complete.

SQL> select * from sec_hrc_audit;

  HRC_CODE   NUM_ROWS
---------- ----------
     4        2
     1        3
     2        3

注意:
  触发器也是一种对象

select * from user_objects where object_type=‘TRIGGER‘;

触发器的字典视图:
select * from user_triggers;        --有整个触发器的详细介绍

(2) update事件的捕获

 创建一个监控表
 create table tr_org_tab as select * from org_tab where 1=2;

create or replace trigger ai_org_trig
  before update on org_tab
create or replace trigger ai_org_trig
  before delete or update or insert on org_tab
  for each row
begin
  if inserting then     --insert事件对应inserting操作
    insert into tr_org_tab values(:NEW.hrc_code,:NEW.org_id,:NEW.org_short_name,:NEW.org_long_name,‘NEW‘,sysdate,‘INSERT‘);
  elsif updating then
    insert into tr_org_tab values(:OLD.hrc_code,:OLD.org_id,:OLD.org_short_name,:OLD.org_long_name,‘OLD‘,sysdate,‘UPDATE‘);
    insert into tr_org_tab values(:NEW.hrc_code,:NEW.org_id,:NEW.org_short_name,:NEW.org_long_name,‘NEW‘,sysdate,‘UPDATE‘);
  elsif deleting then
    insert into tr_org_tab values(:OLD.hrc_code,:OLD.org_id,:OLD.org_short_name,:OLD.org_long_name,‘OLD‘,sysdate,‘DELETE‘);
  end if;
exception when others then
  null;
end;

  for each row
begin
  insert into tr_org_tab values(:OLD.hrc_code,:OLD.org_id,:OLD.org_short_name,:OLD.org_long_name);--更新之前的值
  insert into tr_org_tab values(:NEW.hrc_code,:NEW.org_id,:NEW.org_short_name,:NEW.org_long_name);--更新之后的值
exception when others then
  null;
end;

注意::OLD跟:NEW只要修改了一个值,就能够获取到所修改值那一行的记录的所有字段的值。主键在生产机上只能删除,不能修改,不用考虑主键更新的问题。

测试:

SQL> update org_tab set org_long_name=‘update long‘,org_short_name=‘update short‘ where hrc_code=4;

2 rows updated.

SQL> commit;

Commit complete.

SQL> select * from tr_org_tab;
HRC_CODE    ORG_ID ORG_SHORT_NAME                 ORG_LONG_NAME
-------- --------- ------------------------------ ------------------------------------------------------------
       4      1008 test1                          test2
       4      1008 update short                   update long
       4      1009 test1                          test2
       4      1009 update short                   update long

加两个字段:值的类型:NEW OLD、update的时间

SQL> alter table tr_org_tab add on_flag varchar2(10);           --NEW和OLD字段存储

Table altered.

SQL> alter table tr_org_tab add oper_date date;                 --操作的时间

Table altered.

SQL> truncate table tr_org_tab;

Table truncated.

修改上面的程序
create or replace trigger ai_org_trig
  before update on org_tab
  for each row
begin
  insert into tr_org_tab values(:OLD.hrc_code,:OLD.org_id,:OLD.org_short_name,:OLD.org_long_name,‘OLD‘,sysdate);  --更新之前的值
  insert into tr_org_tab values(:NEW.hrc_code,:NEW.org_id,:NEW.org_short_name,:NEW.org_long_name,‘NEW‘,sysdate);
exception when others then
  null;
end;

SQL> update org_tab set org_long_name=‘update long‘,org_short_name=‘update short‘ where hrc_code=4;

2 rows updated.

SQL> commit;

Commit complete.

验证:

SQL> select * from tr_org_tab;

(3) delete事件的捕获
要求捕获到原值,再加一个字段,写入事件类型:INSERT、UPDATE、DELETE

SQL> alter table tr_org_tab add oper_flag varchar2(10);         --操作的类型

Table altered.

create or replace trigger ai_org_trig
  before delete on org_tab
  for each row
begin
  insert into tr_org_tab values(:OLD.hrc_code,:OLD.org_id,:OLD.org_short_name,:OLD.org_long_name,‘OLD‘,sysdate,‘DELETE‘);  --更新之前的值
exception when others then
  null;
end;

测试:

SQL> delete from org_tab where hrc_code=4;

2 rows deleted.

SQL> commit;

Commit complete.

SQL> select * from tr_org_tab;

(4)将上面的三个触发器合并

create or replace trigger ai_org_trig
  before delete or update or insert on org_tab
  for each row
begin
  if inserting then     --insert事件对应inserting操作
    insert into tr_org_tab values(:NEW.hrc_code,:NEW.org_id,:NEW.org_short_name,:NEW.org_long_name,‘NEW‘,sysdate,‘INSERT‘);
  elsif updating then
    insert into tr_org_tab values(:OLD.hrc_code,:OLD.org_id,:OLD.org_short_name,:OLD.org_long_name,‘OLD‘,sysdate,‘UPDATE‘);
    insert into tr_org_tab values(:NEW.hrc_code,:NEW.org_id,:NEW.org_short_name,:NEW.org_long_name,‘NEW‘,sysdate,‘UPDATE‘);
  elsif deleting then
    insert into tr_org_tab values(:OLD.hrc_code,:OLD.org_id,:OLD.org_short_name,:OLD.org_long_name,‘OLD‘,sysdate,‘DELETE‘);
  end if;
exception when others then
  null;
end;

(5)测试

SQL> insert into org_tab values(4,1008,‘test1‘,‘test2‘);
1 row inserted

SQL> commit;
Commit complete

SQL> update org_tab set org_long_name=‘update long 1‘,org_short_name=‘update short 1‘ where hrc_code=4;
1 row updated

SQL> commit;
Commit complete

SQL> delete from org_tab where org_id=1008;
1 row deleted

SQL> commit;
Commit complete

SQL> select * from tr_org_tab;
HRC_CODE    ORG_ID ORG_SHORT_NAME       ORG_LONG_NAME        ON_FLAG    OPER_DATE   OPER_FLAG
-------- --------- -------------------- -------------------- ---------- ----------- ----------
       4      1008 update short         update long          OLD        2014-06-16  DELETE
       4      1009 update short         update long          OLD        2014-06-16  DELETE
       4      1008 test1                test2                NEW        2014-06-16  INSERT
       4      1008 test1                test2                OLD        2014-06-16  UPDATE
       4      1008 update short 1       update long 1        NEW        2014-06-16  UPDATE
       4      1008 update short 1       update long 1        OLD        2014-06-16  DELETE

实现触发器的时候要注意:

 A 触发器的实现主体中不能够出现commit或者rollback,触发器是自动提交的
 B 触发器内部是不能出现DDL语句。

案例1:限制对departments修改,提示:不允许在非工作时间修改
create or replace trigger tr_dept_time
  before insert or update or delete on departments
  for each row
begin
  if (to_char(sysdate,‘DAY‘) in (‘SATURDAY‘,‘SUNDAY‘)) or (to_char(sysdate,‘HH24:MI‘) not between ‘08:30‘ and ‘18:00‘) then
    raise_application_error(-20001,‘不是上班时间,不能修改‘);
  end if;
exception when others then
  dbms_output.put_line(sqlerrm);
end;

SQL> update departments set department_name=department_name||‘XX‘ where department_id=80;
ORA-20001: 不是上班时间,不能修改

1 row updated.

SQL> select department_name from departments where department_id=80;

DEPARTMENT_NAME
------------------------------
SalesXXXX


仅仅只能发出一个提示,限制不了用户修改表的数据,不能够达到目的

限制用户操作表:用到替代触发器

替代触发器需要建立在视图上面

create or replace view v_dept as select * from departments;

create or replace trigger tr_dept
  instead of insert or update or delete on v_dept
  for each row
begin
  if (to_char(sysdate,‘DAY‘) in (‘SATURDAY‘,‘SUNDAY‘)) or (to_char(sysdate,‘HH24:MI‘) not between ‘08:30‘ and ‘18:00‘) then
    dbms_output.put_line(‘不是上班时间,不能修改‘);
  else
    update departments set department_name=department_name||‘XX‘ where department_id=80;
  end if;
exception when others then
  dbms_output.put_line(sqlerrm);
end;

update v_dept set department_name=department_name||‘XX‘ where department_id=80;

(2) 触发器对字段的捕获

create or replace trigger tr_emp_sal_comm
  before update of salary,commission_pct or delete on employees
  for each row
  when(old.department_id=80)       --对触发条件进行过滤,只针对部门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;
exception when others then
  dbms_output.put_line(sqlerrm);
end;

SQL> update employees set salary=13000 where employee_id=145;
ORA-20001: 部门80员工的工资不能降

1 row updated.

(3)级联修改表

  利用行级触发器实现级联更新,修改主表regions中region_id之后,级联地,自动更新字表中countries表中原来在该地区的region_id

create or replace trigger tr_reg_coun
  after update of region_id on regions               --字段用of,表用on
  for each row
begin
  update countries set region_id=:new.region_id where region_id=:old.region_id;
exception when others then
  null;
end;


测试:
SQL> update regions set region_id=5 where region_id=4;

1 row updated.

SQL> commit;

Commit complete.

但是没有触发器直接改会报错,因为破坏了主外键参考关系

SQL> alter trigger tr_reg_coun disable;         --禁用触发器的命令

Trigger altered.

SQL> update regions set region_id=7 where region_id=5;      --不用触发器,外键约束冲突
update regions set region_id=7 where region_id=5
*
ERROR at line 1:
ORA-02292: integrity constraint (HR.COUNTR_REG_FK) violated - child record found

SQL> alter trigger tr_reg_coun enable;

Trigger altered.

SQL> update regions set region_id=4 where region_id=5;

1 row updated.

SQL> commit;

Commit complete.


触发时机:

 before:表示执行DML之前触发点火,以防止某些错误的操作发生或者实现某些业务规则
 after:表示执行DML之后触发点火,在执行trigger的过程中,这个行会被暂时锁定

触发器可以创建在表或者视图上。

条件谓词:inserting表示insert操作,updating表示update操作、deleting表示delete操作

触发对象:行、字段

##########################################################################################


练习:做一个触发器

捕获org_tab上变化的数据,DML操作都要捕获,捕获每个字段的变化情况,将变化后的值存储在另外一张表中

当在org_tab插入数据的时候,将所有的NEW值捕获到,插入表org_tab_1
当在org_tab修改数据的时候,用NEW值覆盖掉监控表中的OLD值,保留最新的值
当在org_tab删除数据的时候,将对应OLD值删除,逻辑上删除

SQL> desc org_tab_1;
Name           Type         Nullable Default Comments
-------------- ------------ -------- ------- --------
HRC_CODE       NUMBER(4)    Y                         
ORG_ID         NUMBER(8)                              
ORG_SHORT_NAME VARCHAR2(30) Y                         
ORG_LONG_NAME  VARCHAR2(60) Y                         
OPER_TYPE      VARCHAR2(10) Y       --操作的类型                  
OPER_DATE      DATE         Y       --时间                  
OPER_IDX       VARCHAR2(20) Y       --NEW  OLD                  
DEL_FLAG       VARCHAR2(2)  Y       --‘Y‘  删除   ‘N‘  不删除


create table org_tab_1(
hrc_code number(4),
org_id number(8) primary key,
org_short_name varchar2(30),
org_long_name varchar2(60),
oper_type varchar2(10),
oper_date date,
oper_idx varchar2(20),
del_flag varchar2(2)
);

alter table org_tab_1 add constraint FK_ORG foreign key(hrc_code) references hrc_tab(hrc_code);

create or replace trigger tr_org_tab
  before insert or update or delete on org_tab
  for each row
begin
  if inserting then
    insert into org_tab_1 values(:NEW.hrc_code,:NEW.org_id,:NEW.org_short_name,:NEW.org_long_name,‘I‘,sysdate,‘NEW‘,‘N‘);
  elsif updating then
    update org_tab_1
       set hrc_code=:new.hrc_code,
           org_short_name=:new.org_short_name,
           org_long_name=:new.org_long_name,
           oper_type=‘U‘,oper_date=sysdate
     where org_id=:new.org_id;
  elsif deleting then
    update org_tab_1
       set oper_type=‘D‘,
           oper_date=sysdate,
           oper_idx=‘OLD‘,
           del_flag=‘Y‘
     where org_id=:old.org_id;
  end if;
exception when others then
  null;
end;

测试:
SQL> insert into org_tab values(2,1009,‘test1‘,‘test2‘);
1 row inserted

SQL> commit;
Commit complete

SQL> select * from org_tab_1;
HRC_CODE    ORG_ID ORG_SHORT_NAME       ORG_LONG_NAME        OPER_TYPE  OPER_DATE   OPER_IDX             DEL_FLAG
-------- --------- -------------------- -------------------- ---------- ----------- -------------------- --------
       2      1009 test1                test2                I          2014-06-16  NEW                  N

SQL> update org_tab set org_short_name=‘xxxx‘ where org_id=1009;
1 row updated

SQL> commit;
Commit complete

SQL> select * from org_tab_1;
HRC_CODE    ORG_ID ORG_SHORT_NAME       ORG_LONG_NAME        OPER_TYPE  OPER_DATE   OPER_IDX             DEL_FLAG
-------- --------- -------------------- -------------------- ---------- ----------- -------------------- --------
       2      1009 xxxx                 test2                U          2014-06-16  NEW                  N

SQL> delete from org_tab where org_id=1009;
1 row deleted

SQL> commit;
Commit complete

SQL> insert into org_tab values(2,1009,‘test1‘,‘test2‘);
1 row inserted

SQL> commit;
Commit complete

SQL> select * from org_tab_1;
HRC_CODE    ORG_ID ORG_SHORT_NAME       ORG_LONG_NAME        OPER_TYPE  OPER_DATE   OPER_IDX             DEL_FLAG
-------- --------- -------------------- -------------------- ---------- ----------- -------------------- --------
       2      1009 test1                test2                I          2014-06-16  NEW                  N

SQL> update org_tab set org_short_name=‘xxxx‘ where org_id=1009;
1 row updated

SQL> commit;
Commit complete

SQL> select * from org_tab_1;
HRC_CODE    ORG_ID ORG_SHORT_NAME       ORG_LONG_NAME        OPER_TYPE  OPER_DATE   OPER_IDX             DEL_FLAG
-------- --------- -------------------- -------------------- ---------- ----------- -------------------- --------
       2      1009 xxxx                 test2                U          2014-06-16  NEW                  N

SQL> delete from org_tab where org_id=1009;
1 row deleted

SQL> commit;
Commit complete

SQL> select * from org_tab_1;
HRC_CODE    ORG_ID ORG_SHORT_NAME       ORG_LONG_NAME        OPER_TYPE  OPER_DATE   OPER_IDX             DEL_FLAG
-------- --------- -------------------- -------------------- ---------- ----------- -------------------- --------
       2      1009 xxxx                 test2                D          2014-06-16  OLD                  Y
SQL> select * from org_tab_1;
HRC_CODE    ORG_ID ORG_SHORT_NAME       ORG_LONG_NAME        OPER_TYPE  OPER_DATE   OPER_IDX             DEL_FLAG
-------- --------- -------------------- -------------------- ---------- ----------- -------------------- --------
       2      1009 xxxx                 test2                D          2014-06-16  OLD                  Y


###########################################################################################


11.2 用触发器做数据同步、数据镜像、数据更新

  数据同步--逻辑上的数据同步
  数据复制--需要额外的工具或者组件(goldengate)
  数据接口--特殊的数据同步、将数据的一部分传输到临时表上,另外的库定时从临时表上面抓取数据

(1) 数据链路

  A  授权创建

SQL> conn /as sysdba
Connected.
SQL> grant create public database link to plsql;        --所有的用户都能使用,公共数据链路

Grant succeeded.

  B  建立数据链路
   
SQL> conn plsql/plsql
Connected.
SQL> create public database link DBL connect to plsql identified by plsql using ‘ORCL159‘;

Database link created.

 C 本地的表和远端的表作联合查询

SQL> select * from hrc_tab o,org_tab@DBL h where o.hrc_code=h.hrc_code;
HRC_CODE HRC_DESCR            HRC_CODE    ORG_ID ORG_SHORT_NAME                 ORG_LONG_NAME
-------- -------------------- -------- --------- ------------------------------ ------------------------------
       1 CEO/COO                     1      1003 Office of CEO DataPro Inc.     Office of CEO DataPro Inc.
       1 CEO/COO                     1      1002 Office of CEO XYZ Inc.         Office of CEO XYZ Inc.
       1 CEO/COO                     1      1001 Office of CEO ABC Inc.         Office of CEO ABC Inc.
       2 VP                          2      1006 Office of VP Tech ABC Inc.     Office of VP Tech ABC Inc.
       2 VP                          2      1005 Office of VP Mktg ABC Inc.     Office of VP Mktg ABC Inc.
       2 VP                          2      1004 Office of VP Sales ABC Inc.    Office of VP Sales ABC Inc.
 D  对远端的表做数据修改:

SQL> update org_tab@DBL set org_short_name=org_short_name||‘ZZZ‘ where org_id=1006;

1 row updated.

SQL> commit;

Commit complete.

SQL> select * from org_tab@DBL where org_id=1006;
HRC_CODE    ORG_ID ORG_SHORT_NAME                 ORG_LONG_NAME
-------- --------- ------------------------------ ------------------------------
       2      1006 Office of VP Tech ABC Inc.ZZZ  Office of VP Tech ABC Inc.

SQL> select * from org_tab where org_id=1006;
HRC_CODE    ORG_ID ORG_SHORT_NAME                 ORG_LONG_NAME
-------- --------- ------------------------------ ------------------------------
       2      1006 Office of VP Tech ABC Inc.     Office of VP Tech ABC Inc.

这种数据链路也是分布式数据库的基础,数据链路能够平摊磁盘IO,分摊压力,但是这种收到网络带宽的限制

(2) 数据同步

 数据同步可以使用两种发发来实现,第一种使用触发器,第二种使用时间戳的方式

 触发器方法:

  A 创建另外一个数据库实例LAW

  B 在TNS中添加到这个数据库的连接,测试访问

  C 在LAW中创建PLSQL用户,和ORCL实例创建过程一样

[oracle@oracle3 ~]$ export ORACLE_SID=LAW
[oracle@oracle3 ~]$ sqlplus /nolog

SQL*Plus: Release 10.2.0.4.0 - Production on Mon Jun 16 15:17:54 2014

Copyright (c) 1982, 2007, Oracle.  All Rights Reserved.

SQL> conn /as sysdba
Connected.
SQL> select instance_name from v$instance;

INSTANCE_NAME
----------------
LAW

SQL> create tablespace plsql datafile ‘/u01/oracle/oradata/LAW/plsql01.dbf‘ size 1G;    --创建表空间
 
Tablespace created.

SQL> create user plsql identified by plsql default tablespace plsql;        --创建用户并指定表空间

User created.

SQL> grant connect,resource to plsql;

Grant succeeded.

SQL> conn plsql/plsql;
Connected.

D 在ORCL实例上讲PLSQL用户下的org_tab同步到LAW实例的PLSQL用户下

  1)  在LAW的PLSQL用户下创建目标表:ORG_TAB

  创建的脚本可以通过developer工具导出
create table ORG_TAB
(
  hrc_code       NUMBER(4) not null,
  org_id         NUMBER(8) not null,
  org_short_name VARCHAR2(30),
  org_long_name  VARCHAR2(60)
);

alter table ORG_TAB
  add constraint PK_ORG_TAB primary key (HRC_CODE, ORG_ID);
alter table ORG_TAB
  add constraint PK_ORG_TAB_UK unique (ORG_ID);
alter table ORG_TAB
  add constraint ORG_TAB_FK foreign key (HRC_CODE)
  references HRC_TAB (HRC_CODE);

  2) 创建数据链路,在ORCL上面
 
SQL> conn /as sysdba
Connected.
SQL> grant create public database link to plsql;

Grant succeeded.

SQL> conn plsql/plsql
Connected.
SQL> create public database link DBLX connect to plsql identified by plsql using ‘LAW‘;

Database link created.

注意:‘LAW‘--TNSNAME,要配置,因为ORCL和LAW之间要互访(数据链路建立在哪个数据库上,就在哪个数据库的服务器上面配置)

3) 测试数据链路

SQL> select * from org_tab@DBLX;

no rows selected

4) 在原库上的表上面创建触发器  --  抓取表的增量

 全量 : 只要加载一次就可以

create or replace procedure SYN_Q(v_retcd out number,v_errmsg out varchar2) is
  v_time date;
begin
  insert into org_tab@DBLX
  select * from org_tab;
  v_retcd:=0;
  v_errmsg:=‘successful!‘;
  v_time:=trunc(sysdate,‘dd‘);
  insert into sync_audit values(v_time,‘Y‘);
  commit;
exception when others then
  v_retcd:=sqlcode;
  v_errmsg:=sqlerrm;
end;



增量:使用触发器来实现增量的抓取,一天同步一次

create or replace trigger tr_org_tab
  before insert or update or delete on org_tab
  for each row
begin
  if inserting then
    insert into org_tab_1 values(:NEW.hrc_code,:NEW.org_id,:NEW.org_short_name,:NEW.org_long_name,‘I‘,sysdate,‘NEW‘,‘N‘);
  elsif updating then
    update org_tab_1
       set hrc_code=:new.hrc_code,
           org_short_name=:new.org_short_name,
           org_long_name=:new.org_long_name,
           oper_type=‘U‘,oper_date=sysdate
     where org_id=:new.org_id;
  elsif deleting then
    update org_tab_1
       set oper_type=‘D‘,
create or replace procedure SYN_Q(v_retcd out number,v_errmsg out varchar2) is
  v_time date;
begin
  insert into org_tab@DBLX
  select * from org_tab;
  v_retcd:=0;
  v_errmsg:=‘successful!‘;
  v_time:=trunc(sysdate,‘dd‘);
  insert into sync_audit values(v_time,‘Y‘);
  commit;
exception when others then
  v_retcd:=sqlcode;
  v_errmsg:=sqlerrm;
end;
           oper_date=sysdate,
           oper_idx=‘OLD‘,
           del_flag=‘Y‘
     where org_id=:old.org_id;
  end if;
exception when others then
  null;
end;

问题:
  怎样抓取过去一天的数据:

select * from org_tab_1 where oper_date>=trunc(sysdate,‘dd‘)-1 and oper_date<trunc(sysdate,‘dd‘);

抓取前一个小时

select * from org_tab_1 where oper_date>=trunc(sysdate,‘hh‘)-1/24 and oper_date<trunc(sysdate,‘hh‘);

5) 做时间拉链:创建一个数据处理登记表

create table SYNC_AUDIT
(
 AS_DATE DATE not null,
 AS_FLAG varchar2(2)
);
alter table sync_audit add constraint PK_AS_FLAG primary key(AS_DATE);

6)分析如何处理DML操作

   I                    --只要找一个逐渐的只有I一次的,直接插入
   I--U(n)              --找到最后一次U的,插入
   I--U(n)--D           --不处理
   U--U(n)--D           --找到对应的主键删除
   U--U(n)              --找到最后一次U,到目标库上U
   D                    --只要找到一个主键只有D一次,直接删除

7)总结思路:
 
  1、将所有I的直接插入到目标表
  2.   将所有U标记的,找到每个主键修改的U标记的数据,拿到目标库上,存在update,不存在insert
  3、所有D拿出来,直接到目标表上D

8)编写SP
create or replace procedure SYN_Q(v_retcd out number,v_errmsg out varchar2) is
  v_time date;
begin
  insert into org_tab@DBLX
  select * from org_tab;
  v_retcd:=0;
  v_errmsg:=‘successful!‘;
  v_time:=trunc(sysdate,‘dd‘);
  insert into sync_audit values(v_time,‘Y‘);
  commit;
exception when others then
  v_retcd:=sqlcode;
  v_errmsg:=sqlerrm;
end;

create or replace procedure sp_sync is
  v_time1 date;
  v_time2 date;
  v_time3 date;
  x number;
  v_sqlcode number;
  v_sqlerrm varchar2(200);
begin
  /*将sync_audit表上的数据增加到今天*/
  select max(as_date) into v_time3 from sync_audit;
  if trunc(v_time3,‘dd‘)<trunc(sysdate,‘dd‘) then
     loop
       exit when v_time3=trunc(sysdate,‘dd‘);
       insert into sync_audit values(v_time3+1,‘N‘);
       commit;
       v_time3:=v_time3+1;
     end loop;
  end if;

  /*获取增量处理的开始时间和结束时间*/
  select count(1) into x from sync_audit where as_flag=‘Y‘;
  if x <> 0 then
    select as_date into v_time1 from (select as_date
                                        from sync_audit
                                       where as_flag=‘Y‘
                                       order by as_date desc)
                               where rownum=1;
  else
    select min(as_date) into v_time1 from sync_audit;
  end if;
  v_time2:=trunc(sysdate,‘dd‘);

  /*处理增量*/
  /*处理物理删除操作的*/
  delete from org_tab@dblx a
   where exists (select null
                   from org_tab_1 b
                  where b.del_flag=‘Y‘
                    and b.oper_date>=v_time1
                    and b.oper_date<v_time2
                    and a.org_id=b.org_id);
  commit;

  /*处理插入和修改操作的*/
  merge into org_tab@DBLX a1
  using(select * from org_tab_1
  where del_flag=‘N‘ and oper_date>=v_time1 and oper_date<v_time2) a2
  on (a1.org_id=a2.org_id)
  when matched then
    update set
    a1.hrc_code=a2.hrc_code,
    a1.org_short_name=a2.org_short_name,
    a1.org_long_name=a2.org_long_name
  when not matched then
    insert (a1.hrc_code,a1.org_id,a1.org_short_name,a1.org_long_name)
    values (a2.hrc_code,a2.org_id,a2.org_short_name,a2.org_long_name);
  commit;

  /*如果成功没有异常,处理增量时间段的时间拉链全部置为Y*/
  loop
    exit when (v_time1>v_time2);
    update sync_audit set as_flag=‘Y‘ where as_date=v_time1;
    commit;
    v_time1:=v_time1+1;
  end loop;
exception when others then
  v_sqlcode:=sqlcode;
  v_sqlerrm:=sqlerrm;
  insert into exception_monitor values(‘ORG_TAB‘,NULL,upper(‘sp_sync‘),‘OTHERS‘,v_sqlcode,v_sqlerrm,sysdate);
end;

测试:

  A 全量加载
declare
  v_sqlcode number;
  v_sqlerrm varchar2(200);
begin
  syn_q(v_sqlcode,v_sqlerrm);
  dbms_output.put_line(v_sqlerrm);
end;

B 插入、删除、修改原库

insert into org_tab values(4,1007,‘test1‘,‘test2‘);
insert into org_tab values(4,1008,‘test3‘,‘test4‘);
insert into org_tab values(4,1009,‘test5‘,‘test6‘);
commit;

update org_tab set org_short_name=‘test short name‘ where org_id=1008;
commit;

delete from org_tab where org_id=1009;
commit;

create or replace procedure SYN_Q(v_retcd out number,v_errmsg out varchar2) is
  v_time date;
begin
  insert into org_tab@DBLX
  select * from org_tab;
  v_retcd:=0;
  v_errmsg:=‘successful!‘;
  v_time:=trunc(sysdate,‘dd‘);
  insert into sync_audit values(v_time,‘Y‘);
  commit;
exception when others then
  v_retcd:=sqlcode;
  v_errmsg:=sqlerrm;
end;
注意系统时间

begin
  sp_sync;  
end;

select * from org_tab@dblx;

9)创建后台作业

declare
  job number;
begin
  dbms_job.submit(job,‘sp_sync;‘,next_date=>to_date(‘20140620000001‘,‘yyyy-mm-dd hh24:mi:ss‘),interval => ‘sysdate +1‘);
  commit;
end;

确认:
select * from user_jobs;

删除掉后台作业

begin
  dbms_job.remove(job_number);
end;
select * from org_tab@dblx;


以上使用触发器的最大风险是:
 当修改了表的定义的时候,比如添加或者删除字段,此时触发器将失效,编译错误,此时将不点火,这段时间将丢失增量数据

 可以使用时间戳的方式解决以上的风险

(2) 时间戳方式
 
 A 在源库设计的时候每个表上加三个字段
 
  1. 插入时间    2.最后修改的时间     3.删除标记

 B 第一次插入的时候,插入时间=最后修改时间   以后修改只修改‘最后修改时间‘,数据可以逻辑删除

  org_mater中createOrg存储过程要修改,updateOrg存储过程要修改

  removeOrgSite存储过程的delete操作修改为:update org_tab set del_flag=‘1‘ where org_id=ip_org_id;

  获取增量:在原表上按照时间范围就能找到数据,抛弃触发器,直接merge到目标表

  如果删除操作:只能够用全表对比的方式进行更新,性能损耗很大,如果有删除标记,不存在delete操作,一个merge就可以同步。
 
  先同步没有被删除的
 
  再去同步被删除

###########################################################################################

11.3 语句级触发器

statement触发器--语句级触发器

(1)案例:

 A 克隆一个空表

SQL> create table org_tab_2 as select * from org_tab where 1=3;
Table created

 B 创建一个语句级别触发器       --  没法办法获取行的属性 NEW OLD

create or replace trigger ai_org_trig_statement
  after insert on org_tab_2      --语句级触发器的写法,没有for each row
begin
  for idx in (select hrc_code,count(1) as cnt from org_tab_2 group by hrc_code) loop
    update sec_hrc_audit set num_rows=idx.cnt where hrc_code=idx.hrc_code;
    if sql%notfound then
      insert into sec_hrc_audit values(idx.hrc_code,idx.cnt);
    end if;
  end loop;
exception when others then
  null;
end;

 C 测试
SQL> truncate table sec_hrc_audit;

Table truncated.

SQL> insert into org_tab_2 select * from org_tab;

8 rows created.

SQL> commit;

Commit complete.

SQL> select * from sec_hrc_audit;

  HRC_CODE   NUM_ROWS
---------- ----------
     1        3
     2        3
     4        2

(2)行级触发器与语句级触发器的区别
 
      ROW级别触发器                                     statement级别触发器
-----------------------------------------------------------------------------------------------
 触发器每影响一行点火一次                               只为一个SQL语句点火一次
 如果触发器没有影响到任何行触发器不点火             没有影响到行也会点火
 可以指定before或者after                               可以指定before或者after
 可以访问行数据OLD和NEW                             没有办法访问行数据
 after方法触发器锁定行数据                              不会锁定行

##########################################################################################


11.4 一些字句的使用

 referencing字句:只能出现在行级触发器中

create or replace trigger ai_trig_ref
  after insert or update or delete on org_tab
  referencing new as new_org old as old_org   --取代:NEW和:OLD,相当于取别名
  for each row
begin
  update sec_hrc_audit set num_rows=num_rows+1 where hrc_code=:new_org.hrc_code;     --相当于:new.hrc_code
  if sql%notfound then
    insert into sec_hrc_audit values(:new_org.hrc_code,1);
  end if;
exception when others then
  null;
end;

 when字句:

create or replace trigger ai_trig_ref
  after insert or update or delete on org_tab
  referencing new as new_org old as old_org   --取代:NEW和:OLD,相当于取别名
  for each row
  when(new_org.hrc_code<>1)         --<>不等于符号,过滤触发条件,当不等于1时才触发
begin
  update sec_hrc_audit set num_rows=num_rows+1 where hrc_code=:new_org.hrc_code;
  if sql%notfound then
    insert into sec_hrc_audit values(:new_org.hrc_code,1);
  end if;
exception when others then
  null;
end;
 
注意:new_org.hrc_code=1的话,此时触发器不点火
         when字句必须放在for each row 的后面
         when引用new_org.hrc_code前面是没有冒号的

###########################################################################################


11.5 触发器数据字典的结构

SQL> desc user_triggers;
 Name                       Null?    Type
 ----------------------------------------- -------- ----------------------------
 TRIGGER_NAME                        VARCHAR2(30)            --触发器的名称
 TRIGGER_TYPE                        VARCHAR2(16)
 --触发器的类型,可以看出触发的时机还有是什么类型的触发器,行级还是语句级的
 TRIGGERING_EVENT                    VARCHAR2(227)           --触发器事件类型
 TABLE_OWNER                        VARCHAR2(30)
 BASE_OBJECT_TYPE                    VARCHAR2(16)            --操作的对象(table)
 TABLE_NAME                            VARCHAR2(30)            --表名
 COLUMN_NAME                        VARCHAR2(4000)
 REFERENCING_NAMES                    VARCHAR2(128)
 WHEN_CLAUSE                        VARCHAR2(4000)
 STATUS                             VARCHAR2(8)             --触发器的状态,表示启用还是禁用
 DESCRIPTION                        VARCHAR2(4000)          --触发器的描述,也就是头部的语句
 ACTION_TYPE                        VARCHAR2(11)
 TRIGGER_BODY                        LONG                    --触发器的内容

 select * from user_objects where object_type=‘TRIGGER‘;        --概要信息

 触发器的状态:在user_objects这个视图中,触发器的状态表示有效的和无效的 VALID 和INVALID
                       在user_triggers视图中,触发器的状态表示启用还是禁用ENABLE 和 DISABLE

###########################################################################################


11.6 触发器的管理

(1) 启用、禁用、编译触发器

SQL> alter trigger tr_org_tab disable;          --禁用触发器

Trigger altered
 
select * from user_triggers where trigger_name=‘TR_ORG_TAB‘;        --status变为 DISABLE

SQL> alter trigger tr_org_tab enable;           --启用触发器
Trigger altered

select * from user_triggers where trigger_name=‘TR_ORG_TAB‘;        --status变为 ENABLE

当触发器失效的时候要重新编译,而且还是要能够编译通过,否则触发器不点火

情景:给org_tab_2表添加一个字段:
SQL> alter table org_tab_2 add x number;

Table altered.

select * from user_objectS where object_name=‘AI_ORG_TRIG_STATEMENT‘;  --status为INVALID

SQL> alter trigger AI_ORG_TRIG_STATEMENT compile;       --重新编译触发器,使触发器生效

Trigger altered.

select * from user_objectS where object_name=‘AI_ORG_TRIG_STATEMENT‘;  --status为VALID

(2) 启用、禁用某个表上所有的触发器

SQL> alter table org_tab disable all triggers;

Table altered.

select * from user_triggers where table_name=‘ORG_TAB‘;     --验证 DISABLE

SQL> alter table org_tab enable all triggers;

Table altered.

select * from user_triggers where table_name=‘ORG_TAB‘;     --验证 ENABLE

(3) 关于触发器的权限

A 本用户下的触发器:create trigger、alter trigger、drop trigger

B 系统内: create any trigger、alter any trigger、drop any trigger

(4)触发器的限制:
 
 A 触发器的脚本大小不能够超过32K
 B 触发器内部不能够写DDL语句
 C 不能出现commit或者rollback
 D LONG字段不能用行级触发器
 E 修改信息的时候违反了参考完整性,触发器会点火但是内部不执行

###########################################################################################


11.7 DDL事件触发器

 捕获的事件是DDL事件

 (1) 对用户授权

SQL> conn /as sysdba
Connected.
SQL> grant create any trigger to plsql;

Grant succeeded.

SQL> grant alter any trigger to plsql;

Grant succeeded.

SQL> grant drop any trigger to plsql;

Grant succeeded.

SQL> grant ADMINISTER DATABASE TRIGGER to plsql;

Grant succeeded.

 (2) 创建触发器
 
 方法1:通过sys系统变量创建DDL事件捕获触发器

 创建一个监控表

create table ddl_audit(
object_name varchar2(30) not null,          --DDL对象名
object_type varchar2(30) not null,          --DDL的对象类型
when_created date not null,                 --DDL的时间
who_created varchar2(30) not null,          --操作DDL的用户
op_type varchar2(30) not null               --DDL类型
);

create or replace trigger after_ddl_creation
  after create or alter or drop on schema
begin
  insert into ddl_audit values(sys.dictionary_obj_name,sys.dictionary_obj_type,sysdate,sys.dictionary_obj_owner,sys.sysevent);
exception when others then
  null;
end;

函数:                                                                                                      同义词
sys.dictionary_obj_name  -- sys用户下的一个加密函数,返回操作的对象的名称             ora_dict_obj_name
sys.dictionary_obj_type  -- 返回的是操作对象的对象类型                                        ora_dict_obj_type
sys.dictionary_obj_owner -- 返回的是操作DDL的用户                                         ora_dict_obj_owner
sys.sysevent                     -- 返回DDL的类型,create、alter、drop                        ora_sysevent

SQL> conn plsql/plsql
Connected.
SQL> create table x(u int);

Table created.

SQL> drop table x;

Table dropped.

SQL> create table y(u int);

Table created.

SQL> alter table y add yu int;

Table altered.

SQL> select * from ddl_audit;
OBJECT_NAME                    OBJECT_TYPE                    WHEN_CREATED WHO_CREATED                    OP_TYPE
------------------------------ ------------------------------ ------------ ------------------------------ -------
X                              TABLE                          2014-06-19 1 PLSQL                          CREATE
X                              TABLE                          2014-06-19 1 PLSQL                          DROP
Y                              TABLE                          2014-06-19 1 PLSQL                          CREATE
Y                              TABLE                          2014-06-19 1 PLSQL                          ALTER

方法2:使用同义词的方式
 
 synonym 同义词

 案例:将数据链路封装在同义词

先授权
SQL> conn /as sysdba
Connected.
SQL> grant create synonym to plsql;

Grant succeeded.

SQL> create or replace synonym "PLSQL"."SYNC" for "ORG_TAB"@"DBLX";
Synonym created

select * from org_tab@DBLX;
select * from sync;


create or replace trigger after_ddl_creation
  after create or alter or drop on schema
begin
  insert into ddl_audit values(ora_dict_obj_name,ora_dict_obj_type,sysdate,ora_dict_obj_owner,ora_sysevent);
exception when others then
  null;
end;

###########################################################################################


11.8 系统事件触发器

(1) 捕获系统事件

 普通用户想要捕获系统事件,必须要有dba权限

SQL> grant dba to plsql;

Grant succeeded.

  after logon on database     --登录系统后
  before logoff on database   --退出系统前
  before shutdown on database --关机前
  after startup on database   --开机后

(2) 同义词

ora_client_ip_address     --操作的客户端的ip地址
ora_database_name         --操作的数据库的实例名
ora_login_user            --登录操作的用户
ora_sql_txt               --操作的文本

(3) 创建一个监控表
create table log_event(
user_name varchar2(10),
logon_date timestamp,
logoff_date timestamp
);

(4) 创建一个触发器

create or replace trigger tr_login
  after logon on database
begin
  insert into log_event values(ora_login_user,systimestamp,null);
exception when others then
  null;
end;

Systimestamp 函数返回本机数据库上当前系统日期和时间(包括微秒和时区)

(5) 测试

SQL> conn plsql/plsql;
Connected.
SQL> disconnect
Disconnected from Oracle Database 10g Enterprise Edition Release 10.2.0.4.0 - Production
With the Partitioning, OLAP, Data Mining and Real Application Testing options
SQL> show user
USER is ""
SQL> conn scott/scott;
Connected.
SQL> conn plsql/plsql;
Connected.

查看:
SQL> select * from log_event;

练习1:

 A 创建捕获用户退出事件的触发器
 B 在监控表上加上ORA_DATABASE_NAME,ORA_CLIENT_IP_ADDRESS

SQL> conn plsql/plsql@ORCL
Connected.
SQL> select sys_context(‘userenv‘,‘ip_address‘) from dual;

SYS_CONTEXT(‘USERENV‘,‘IP_ADDRESS‘)
--------------------------------------------------------------------------------
10.1.3.3


练习2:
 创建捕获数据库关闭的触发器

 要求捕获关闭数据库的名称、执行关闭的用户、以及客户端的IP地址、另外还要捕获操作的时间

###########################################################################################


plsql触发器

标签:replace   commit   before   insert   create   

原文地址:http://fengsonglin.blog.51cto.com/9860507/1615261

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!