一、PL/SQL触发器
一种数据库对象,它是一种发生在预先定义的数据库事件时自动运行的PL/SQL代码块。
分为触发条件和被触发事件,条件满足时,被触发事件立即执行。(实际当中用的不多,DML触发器知道怎么用,其他了解即可)
分类:
1、DML触发器
CREATE [OR] REPLACE TRIGGER 触发器名
AFTER|BEFORE
[INSERT/UPDATE/DELETE]
ON 表名
[FOR EACH ROW]
[WHEN 条件]
BEGIN
...
END
例题1 :创建一个触发器,实现:部门表的部门编号改变时,相对应员工表的部门编号也要改变。
CREATE OR REPLACE TRIGGER tr_upd_dept
AFTER
UPDATE ON dept
FOR EACH ROW--代表行级别触发器,一般DML和替代触发器会使用(对应还有语句级触发器)
BEGIN
UPDATE emp SET deptno=:new.deptno WHERE deptno=:old.deptno;--这里引入两张表:
--:old此表结构和出发条件表结构一样,存放老值(update、delete);
--:new此表结构和出发条件表结构一样,存放新值(insert、update);
END;
UPDATE dept SET deptno=22 WHERE deptno=20;
2、DDL触发器
例题2:当你创建一张表时,系统会打印'用户..在..时间..了..'
CREATE OR REPLACE TRIGGER tr_ddl_prt
AFTER DDL ON SCHEMA
BEGIN
dbms_output.put_line(USER||'于'||SYSDATE||ora_sysevent||'了'||ora_dict_obj_type||':'||ora_dict_obj_name);
END;
CREATE TABLE tr_test1 (ID NUMBER(8));
DROP TABLE tr_test;
DROP TABLE tr_test1;
3、替代触发器
更新emp_view视图的loc字段,使'DALLAS'更改为'dallas';
CREATE OR REPLACE TRIGGER tr_empv_upd
INSTEAD OF UPDATE ON emp_view
FOR EACH ROW
BEGIN
UPDATE dept SET loc=:new.loc WHERE loc=:old.loc;
END;
UPDATE emp_view SET loc='dallas' WHERE loc='DALLAS';
SELECT * FROM emp_view;
4、事件触发器
(1)系统事件触发器
例题,创建系统触发器,记录系统每次启动的时间;
CREATE TABLE dbstart_log(ev_date DATE);
CREATE OR REPLACE TRIGGER tr_db_starttime
AFTER startup ON DATABASE
BEGIN
INSERT INTO dbstart_log VALUES(SYSDATE);
END;
(2)用户事件触发器
CREATE TABLE logon_log(logon_date DATE);
CREATE OR REPLACE TRIGGER tr_user_logon
AFTER logon ON SCHEMA
BEGIN
INSERT INTO logon_log VALUES(SYSDATE);
END;
SELECT * FROM logon_log;
二、定时任务 JOB
实际开发中需定时完成某项任务,比如之前学的存储过程主要对数据做抽取、转换工作,可以设置定时间点抽取。
job语法;--这里其实就是调用系统内置的job包创建一个job
dbms_job.submit(
ob OUT BINARY_INTEGER,--任务id
what IN VARCHAR2, --做什么
next_date IN DATE DEFAULT sysdate,--第一次执行时间
interval IN VARCHAR2 DEFAULT 'null',--执行周期
);
系统 dbms_job.submit定义:
PROCEDURE submit ( job OUT BINARY_INTEGER,
what IN VARCHAR2,
next_date IN DATE DEFAULT sysdate,
interval IN VARCHAR2 DEFAULT 'null',
no_parse IN BOOLEAN DEFAULT FALSE,
-- Bug 1346620: replace pkg vars with constants.
-- Default for instance = dbms_job.any_instance.
instance IN BINARY_INTEGER DEFAULT 0,
force IN BOOLEAN DEFAULT FALSE );
示例:
DECLARE
job NUMBER;
BEGIN
dbms_job.submit(
job,
'prc_test;',--做什么;
to_date('20241215 14:22','yyyymmdd hh24:mi'),--第一次执行时间;
'sysdate+1/(24 * 60)'--执行周期为每分钟,sysdata+1直接加1表示加一天;
);
COMMIT;
END;
CREATE OR REPLACE PROCEDURE prc_test
IS
BEGIN
INSERT INTO dept_bak VALUES(to_number(to_char(SYSDATE,'mi')),'a','b');
COMMIT;
END;
三、数据字典
数据字典
是有关于数据库的参考信息、结构信息、对象信息等一组表或视图的集合,由系统维护。
--常用的:
--所有表
SELECT * FROM user_tables;--查询当前用户(schema)下的所有表
SELECT * FROM all_tables;--当前用户有权限可查看的所有表(包含自建和别人创建的)
SELECT * FROM dba_tables;--DBA用户查看的
user_procedures
user_triggers
user_sequences
user_indexes
user_constraints
user_views
user_db_links
user_synonyms
SELECT * FROM user_objects;--查询用户下所有的数据库对象
---数据字典种类:
USER 视图: 记录用户对象信息
ALL 视图:记录用户对象和被授权访问的对象的信息
DBA 视图:记录数据库实例下的所有对象信息
V$视图: 记录与数据库活动相关信息的性能动态统计信息
GV$视图:记录分布式环境下的所有实例的动态信息
四、递归算法
本质就是解决oracle中树形结构数据的查询问题
实际问题:银行项目中 总行-分行-支行-网点机构
1、根找子(找出7566下所有下属)
SELECT empno,mgr
FROM emp
CONNECT BY mgr =PRIOR empno
START WITH empno=7566;
2、子找根(找出编号7369的所有领导)
SELECT empno,mgr
FROM emp
CONNECT BY empno= PRIOR mgr
START WITH empno=7369;
3、sys_connect_by_path(查找路径) 和 connect_by_isleaf(没有分支了为1)
SELECT empno,mgr,sys_connect_by_path(mgr,'->'),connect_by_isleaf
FROM emp
CONNECT BY empno= PRIOR mgr
START WITH empno=7369;
4、LEVEL(级别)
SELECT LEVEL,emp.*
FROM emp
CONNECT BY mgr=PRIOR empno
START WITH mgr IS NULL;
五、分析函数
分析函数,本质上跟我们之前学的标量函数、自定义函数是一样的,都是先分组,再对列进行处理;
不同的是:之前的函数,是需要严格按照Oracle语法,先分组,再计算,这时候,必然会导致数据量少了,只有在group by后的字段才可以跟在select后面;
分析函数就可以让我们保留每行数据,使每行数据既有明细数据,也有分组后的计算数据。
--语法: 分析函数 over([partition by.. 可以是多个字段][order by..])
支持的聚合函数:
1、sum、max,min,avg,count
2、lead() 统计窗口内往上第n行值,用于获取当前行之后的行的值,n默认为1;
lag() 统计窗口内往下第n行值,用于获取当前行之前的行的值,n默认为1;
3、first_value():分组后的第一个值;
last_value():分组后的最后一个值,截至到当前行
序号函数:
row_number() 值相同,返回不同序号;
rank() 值相同,跳跃排序:1224;
dense_rank() 值相同,非跳跃排序:1223;
--例1:显示各个员工编号、姓名、部门编号、所在部门人数
以前的写法:通过连接子查询(部门编号、部门人数)实现
SELECT e.empno,e.ename,e.sal,e.deptno,d.cnt
FROM emp e,(SELECT deptno,count(*) cnt FROM emp GROUP BY deptno) d
WHERE e.deptno=d.deptno;
通过分析函数方式写法:
SELECT empno,ename,sal,deptno,COUNT(*) OVER(PARTITION BY deptno)
FROM emp;
--例2:查询员工编号、姓名、部门、工资,工资累计之和(所在部门内按员工编号升序后的累计)
SELECT empno,ename,deptno,sal,SUM(sal) OVER(PARTITION BY deptno order by empno)
FROM emp;
注:如果既partition by 分组又order by 排序,则分组函数是计算组内的累计值,例如再有
SELECT ename,deptno,sal,AVG(sal) OVER(PARTITION BY deptno ORDER BY sal) FROM emp;
得到结果如下:可见deptno为10的部门中,第二个 名为CLARK的 avg(sal)是, 本人sal 2450 和 第一个MILLER的1300 得到的平均值:1875;
再举个例子如下:最晚雇佣日期lastdate是本组内,从上到下,截止到自己为止,最晚的日期;
例3:查询员工编号,姓名,工资,工资排名(降序)
--相同sal的排名不同1234
SELECT empno,ename,sal,row_number() OVER(ORDER BY sal DESC)
FROM emp;
--1224排名
SELECT * FROM emp;
SELECT empno,ename,sal,RANK() OVER(ORDER BY sal DESC )
FROM emp;
--正常排名1223
SELECT empno,ename,sal,dense_rank() OVER(ORDER BY sal DESC)
FROM emp;
例4、查找每个员工姓名,部门号,雇佣日期,以及所在部门内 上个人的和下个人的雇佣日期进行对比:
SELECT ename,deptno,hiredate,
LAG(hiredate) OVER(PARTITION BY deptno ORDER BY hiredate) predate,
LEAD(hiredate) OVER(PARTITION BY deptno ORDER BY hiredate) nextdate
FROM emp;
注:实际开发中,可能会用来查看用户上一次登录时间,计算登录时间间隔,判断是否为活跃用户。
例5、查询每个部门工资最高的员工信息;
SELECT *
FROM
(SELECT e.*,RANK() OVER(PARTITION BY deptno ORDER BY sal DESC) salno FROM emp e)
WHERE salno=1;