第一个包
SQL> create or replace package pk1
2 is
3 procedure p;
4 end;
5 /
程序包已创建。
SQL> create or replace package body pk1
2 is
3 procedure p
4 is
5 begin
6 for i in(select * from v1) loop
7 null;
8 --啥也不做
9 end loop;
10 end;
11 end;
12 /
程序包体已创建。
2 第2个包
QL> create or replace package pk2
2 is
3 procedure p ;
4 end;
5 /
程序包已创建。
依赖于第一个包
1 create or replace package body pk2
2 is
3 procedure p
4 is
5 begin
6 pk1.p;
7 end;
8* end;
SQL> /
程序包体已创建。
查看他们之间的依赖性
select name,type,referenced_name,referenced_type
2 from
3 user_depeNDENCIES
4 where referenced_owner='SCOTT'
5* and name in('T1','V1','P1','P2')
SQL> /
NAME TYPE REFERENCED_NAME
---------- -------------------- ----------------------------------------------------------------
REFERENCED_TYPE
------------------
P2 PROCEDURE P1
PROCEDURE
V1 VIEW T1
TABLE
P1 PROCEDURE V1
2 查看他们的有效性
修改之后查看他们的有效性
SQL> alter table t1 modify(id int);
表已更改。
SQL> select object_name,object_type,status from user_objects
2 where object_name in('T1','V1','PK1','PK2');
OBJECT_NAME
OBJECT_TYPE STATUS
V1
VIEW INVALID
T1
TABLE VALID
PK2
PACKAGE BODY VALID
PK2
PACKAGE VALID
PK1
PACKAGE BODY INVALID
PK1
PACKAGE VALID
已选择6行。
触发器
触发器是一种特殊的过程,它的执行时有一系列事件触发的 这些事件有用户登录注销事件,DML 语句执行事件等等 我们常用的就是DML触发器 事件的准确粒度可以是表现或行级
trigger 的实际应用非常广泛例如A有数据DML操作,就可以在A表上做个触发器,将数据协同更新到B表
我们也经常用触发器来做审计
1 create or replace trigger tri_t1_no_dml
2 before update or delete or insert
3 on t1
4 begin
5 if updating then
6 raise_application_error(-20001,'no update allowed on this table');
7 elsif deleting then
8 raise_application_error(-20002,'no delete allowed on this table');
9 else
10 raise_application_error(-20003,'no insert allowed on this table');
11 end if;
12* end;
SQL> /
触发器已创建
SQL> desc user_source; 查看结构
名称 是否为空? 类型
----------------------------------------------------- -------- ------------------------------------
NAME VARCHAR2(30)
TYPE VARCHAR2(12)
LINE NUMBER
TEXT VARCHAR2(4000)
查看源代码
col text for a30;
SQL> select text from user_source where name='TRI_T1_NO_DML';
select trigger_name,trigger_type,triggering_event,table_name,status from user_triggers;
TRIGGER_NAME TRIGGER_TYPE
TRIGGERING_EVENT
TABLE_NAME STATUS
TRI_T1_NO_DML BEFORE STATEMENT
INSERT OR UPDATE OR DELETE
T1 ENABLED
SQL> col triggering_event for a30
SQL> select trigger_name,trigger_type,triggering_event,table_name,status from user_triggers;
TRIGGER_NAME TRIGGER_TYPE TRIGGERING_EVENT
------------------------------ ---------------- ------------------------------
TABLE_NAME STATUS
------------------------------ --------
TRI_T1_NO_DML BEFORE STATEMENT INSERT OR UPDATE OR DELETE
T1 ENABLED
3 然后禁用触发器事件
SQL> alter trigger tri_t1_no_dml disable;
触发器已更改
update t1 set sal=sal+1;
已更新15行。
SQL> commit ;
提交完成。
SQL> alter trigger tri_t1_no_dml enable;
触发器已更改
SQL> update t1 set sal=sal+1;
update t1 set sal=sal+1
*
第 1 行出现错误:
ORA-20001: no update allowed on this table
ORA-06512: 在 "SCOTT.TRI_T1_NO_DML", line 3
ORA-04088: 触发器 'SCOTT.TRI_T1_NO_DML' 执行过程中出错
写一个触发器是使用参数为user sysdate
SQL> create table t2 as select * from emp;
表已创建。
SQL>
SQL>
SQL> create table audit_log(
2 oper_time date,
3 oper_user varchar2(20),
4 oper_type varchar2(20));
表已创建。
SQL> select user from dual;
USER
SCOTT
SQL> select user,sysdate from dual;
USER SYSDATE
SCOTT 17-7月 -21
1 create or replace trigger tri_t2
2 after update or delete or insert on t2
3 declare
4 begin
5 if updating then
6 insert into audit_log values(
7 sysdate,
8 user,
9 'update');
10 elsif deleting then
11 insert into audit_log values(
12 sysdate,
13 user,
14 'delete');
15 else
16 insert into audit_log values(
17 sysdate,
18 user,
19 'insert');
20 end if;
21* end;
SQL> /
SQL> update t2 set sal=0 where empno=7788;
已更新 1 行。
SQL> select * from audit_log ;
OPER_TIME OPER_USER OPER_TYPE
17-7月 -21 SCOTT update
其次我们使用sys来测试一下
C:\Users\23662>sqlplus sys/bwf123456 AS SYSDBA
SQL> insert into scott.t2(empno) values(1001);
已创建 1 行。
SQL> commit;
提交完成。
此时我们在触发器中加入for each row 之后看看什么变化
insert into t2 select * from emp where deptno=10;
已创建3行。
SQL> select * from t2;
QL> select * from audit_log ;
OPER_TIME OPER_USER OPER_TYPE
-------------- -------------------- --------------------
17-7月 -21 SCOTT update
17-7月 -21 SCOTT insert
17-7月 -21 SCOTT insert
17-7月 -21 SCOTT insert
17-7月 -21 SYS insert
查看触发器
select trigger_name,trigger_type,triggering_event,table_name,status from user_triggers;
TRIGGER_NAME TRIGGER_TYPE TRIGGERING_EVENT
------------------------------ ---------------- ------------------------------
TABLE_NAME STATUS
------------------------------ --------
TRI_T2 AFTER EACH ROW INSERT OR UPDATE OR DELETE
T2 ENABLED
TRI_T1_NO_DML BEFORE STATEMENT INSERT OR UPDATE OR DELETE
T1 ENABLED
创建一个表
SQL> ed
已写入 file afiedt.buf
1 create or replace trigger tri_t3_tab_after
2 after update on t3
3 begin
4 dbms_output.put_line('你已经更新此表l');
5* end;
SQL> /
已写入 file afiedt.buf
1 create or replace trigger tri_t3_tab_after
2 after update on t3
3 begin
4 dbms_output.put_line('你已经更新此表l');
5* end;
SQL> /
触发器已创建
SQL> select trigger_name,trigger_type,triggering_event,table_name,status from user_triggers;
SQL> create table t2 as select * from emp;
表已创建。
触发器已创建
SQL> select trigger_name,trigger_type,triggering_event,table_name,status from user_triggers;
启用t3上的所有触发器
SQL> alter table t3 enable all triggers;
表已更改。
SQL> select trigger_name,trigger_type,triggering_event,table_name,status from user_triggers;
触发器2
1 create or replace trigger tri_t4
2 before update or delete or insert
3 on t4
4 for each row
5 --每一行都要
6 begin
7 if updating then
8 dbms_output.put_line(:old.sal||','||:new.sal);
9 elsif deleting then
10 dbms_output.put_line(:old.sal||','||:new.sal);
11 else
12 dbms_output.put_line(:old.sal||','||:new.sal);
13 end if;
14* end;
SQL> /
触发器已创建
QL> update t4 set sal=7000 where empno=7788;
7000,7000
已更新 1 行。
练习
1 删除d表表时将edeptno 置空 2 更新d表 deptno 时自动更新e表deptno 3 删除d表数据时将e表痛部门员工删除
1)SQL> create table e as select * from emp;
表已创建。
SQL> create table d as select * from dept;
表已创建。
1 create or replace trigger tri_d_1
2 after delete on d
3 for each row
4 begin
5 update e set deptno=null where deptno=:old.deptno;
6* end;
SQL> /
触发器已创建
1 create or replace trigger tri_d_2
2 after update on d
3 for each row
4 begin
5 update e set deptno=:new.deptno where deptno=:old.deptno;
6* end;
SQL> /
SQL> update d set deptno=80 where deptno=20;
已更新 1 行。
create or replace trigger tri_d_3
2 after delete on d
3 for each row
4 begin
5 delete e where deptno=:old.deptno;
6* end;
SQL> /
触发器已创建
SQL> delete d where deptno=20;
已删除 1 行。
自治事务
查看审计
SQL> select * from audit_log;
OPER_TIME OPER_USER OPER_TYPE
-------------- -------------------- --------------------
17-7月 -21 SCOTT update
17-7月 -21 SCOTT insert
17-7月 -21 SCOTT insert
17-7月 -21 SCOTT insert
17-7月 -21 SYS insert
SQL> delete audit_log;
已删除5行。
SQL> commit;
提交完成。
SQL> update t2 set sal=sal+1;
已更新19行。
SQL> select * from audit_log;
OPER_TIME OPER_USER OPER_TYPE
-------------- -------------------- --------------------
18-7月 -21 SCOTT update
18-7月 -21 SCOTT update
18-7月 -21 SCOTT update
18-7月 -21 SCOTT update
2 ) 查看之前的触发器是怎么写的
SQL> select text from user_source where name='TRI_T2';
TEXT 前面加上 create or replace 和在后面end之前加上commit r2 ) 在声明的部分加上
pragma autonomous_transaction;
trigger tri_t2
after update or delete or insert on t2
for each row
declare
pragma autonomous_transaction;
begin
if updating then
insert into audit_log values(
sysdate,
user,
'update');
elsif deleting then
insert into audit_log values(
sysdate,
user,
'delete');
else
insert into audit_log values(
sysdate,
user,
'insert');
end if;
commit;
end;
已选择22行。
加上 pragma autonomous_transaction; 之后再次测试
QL> update t2 set sal=sal+1;
已更新19行。
里面的工作没人增加了1块钱
SQL> select * from t2;
EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO
---------- ---------- --------- ---------- -------------- ---------- ---------- ----------
7369 SMITH CLERK 7902 17-12月-80 1501
2 ) 同时也更新了数据
SQL> select * from audit_log;
OPER_TIME OPER_USER OPER_TYPE
-------------- -------------------- --------------------
18-7月 -21 SCOTT update
18-7月 -21 SCOTT update
当rollback时
QL> rollback;
回退已完成。
工资是回退了但是审计依然在 这就是 自治事务和触发器分开
SQL> select * from audit_log;
OPER_TIME OPER_USER OPER_TYPE
-------------- -------------------- --------------------
18-7月 -21 SCOTT update
18-7月 -21 SCOTT update
3代替触发器
SQL> create view v3 as select empno,ename,sal,d.deptno,dname,loc from d,e where d.deptno=e.deptno;
视图已创建。
SQL> select * from v3;
EMPNO ENAME SAL DEPTNO DNAME LOC
---------- ---------- ---------- ---------- -------------- -------------
8001 jason 2000 30 SALES CHICAGO
7900 JAMES 1500 30 SALES CHICAGO
7844 TURNER 4500 30 SALES CHICAGO
7698 BLAKE 4500 30 SALES CHICAGO
7654 MARTIN 4500 30 SALES CHICAGO
7521 WARD 4500 30 SALES CHICAGO
7499 ALLEN 4500 30 SALES CHICAGO
已选择7行。
SQL> insert into v3 values(
2 8003,'bear',3000,40,'sales','tj');
insert into v3 values(
第 1 行出现错误:
ORA-01779: 无法修改与非键值保存表对应的列
我们发现是不能对里面进行任何的操作的
1 create or replace trigger tri_v3
2 instead of insert on v3
3 begin
4 if emp_pack.valid_dept(:new.deptno) then
5 insert into e (empno,ename,sal,deptno)
6 values(
7 :new.empno,:new.ename,:new.sal,:new.deptno);
8 else
9 insert into e (empno,ename,sal,deptno)
10 values(
11 :new.empno,:new.ename,:new.sal,:new.deptno);
12 insert into d
13 values(
14 :new.deptno,:new.dname,:new.loc);
15 end if;
16* end;
17 /
触发器已创建
3 ) 此时我么再次测试已经成功了
SQL> insert into v3 values(
2 8003,'bear',3000,40,'sales','tj');
已创建 1 行。
已写入 file afiedt.buf
1 insert into v3 values(
2* 8004,'tiger',3000,90,'sales','nanjing')
/
然后在v3 d e表中都以看到插入的数据
select * from d;
DEPTNO DNAME LOC
---------- -------------- -------------
30 SALES CHICAGO
40 OPERATIONS BOSTON
60 sales bj
61 teacher tj
62 sales shanghai
41 test1
90 sales nanjing
系统触发器
SQL> conn scott/bwf123456@localhost/orcl
已连接。
SQL>
SQL> select sys_context('userenv','ip_address') from dual;
SYS_CONTEXT('USERENV','IP_ADDRESS')
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
127.0.0.1
触发事件 触发实际 触发条件
Lonon After 用户登录成功后
Logoff Before 用户退出登录前
Startup After 数据库启动后
Shutdown Before 数据库关闭前
Servererror After 系统发生故障后
Logon / Logoff 触发器可以用来记录用户登入和退出的时间
数据库启动和关闭触发器可以用来记录一些数据库启动后和关闭前的前处理和后处理
Serverroe 触发器可以用于记录某些重要的错误信息,以便于跟踪系统,发生故障
QL> create table log_audit
2 (
3 logon_time date,
4 logoff_time date,
5 log_user varchar2(20),
6 ipaddr varchar2(20),
7 err_code varchar2(10));
已写入 file afiedt.buf
登录的触发器
1 create or replace trigger tri_logon
2 after logon on database
3 begin
4 insert into log_audit(logoff_time,log_user,ipaddr)
5 values(sysdate,user,sys_context('userenv','ip_address'));
6* end;
SQL> /
触发器已创建
这是退出的触发器
1 create or replace trigger tri_logoff
2 before logoff on database
3 begin
4 insert into log_audit(logoff_time,log_user,ipaddr)
5 values(sysdate,user,sys_context('userenv','ip_address'));
6* end;
SQL> /
触发器已创建
SQL> conn scott/bwf123456@localhost:1521/orcl
已连接。
SQL> select * from log_audit;
LOGON_TIME LOGOFF_TIME LOG_USER IPADDR ERR_CODE
-------------- -------------- -------------------- -------------------- ----------
18-7月 -21 SYS
8-7月 -21 SCOTT
18-7月 -21 SCOTT
18-7月 -21 SCOTT 127.0.0.1
错误日志
1 create or replace trigger err_track
2 after servererror on database
3 begin
4 if is_servererror(1017) then
5 insert into log_audit(logon_time,log_user,ipaddr,err_code)
6 values(sysdate,user,sys_context('userenv','ip_address'),'1017');
7 elsif is_servererror(2449) then
8 insert into log_audit(logon_time,log_user,ipaddr,err_code)
9 values(sysdate,user,sys_context('userenv','ip_address'),'2449');
10 end if;
11* end;
12 /
触发器已创建
我们在另外一个端口故意登录错误
SQL> conn scott/qweqw@localhost:1521/orcl
ERROR:
ORA-01017: invalid username/password; logon denied
警告: 您不再连接到 ORACLE。
> select * from log_audit; 查看错误日志
LOGON_TIME LOGOFF_TIME LOG_USER IPADDR ERR_CODE
-------------- -------------- -------------------- -------------------- ----------
18-7月 -21 SCOTT
18-7月 -21 127.0.0.1 1017
18-7月 -21 SYS
18-7月 -21 SYS
2 )SQL> conn scott/bwf123456@localhost:1521/orcl
已连接。
SQL> drop table dept;
drop table dept
*
第 1 行出现错误:
ORA-02449: 表中的唯一/主键被外键引用
SQL>
3 )再次查看
select * from log_audit;
LOGON_TIME LOGOFF_TIME LOG_USER IPADDR ERR_CODE
18-7月 -21 SCOTT 127.0.0.1 2449
只允许在某个IP上去登录