Oracle数据库的分析函数、层次化查询

701 阅读5分钟

1、Oracle分析函数

分析函数格式: 功能函数() over()

  • 1、over()分析函数 -- 查询每个部门的员工信息,以及其占所有员工工资的百分比 -- 查询原理:当select后面要查询的结果集为功能函数时,没有进行分组,就默认是一个结果。但是要查询多个结果,只能复制功能函数结果, -- over()函数作用:复制功能函数结果, -- 使用over()分析函数 进行复制
select e.*,round(sal/sum(sal) over() * 100,2)|| '%' per_sal
from emp e
  • 2、partition by 子句: 分区(分组)子句(只能在over() 函数中使用) -- group by: 会影响查询结果集的行数 -- partition by : 不会影响查询结果集的行数 -- 作用,在指定的分区内进行功能函数结果的复制 -- 查询每个部门的员工信息,以及其占本部门员工工资的百分比

    select e.*,round(sal/sum(sal) over(partition by deptno)*100,2) || '%' per_sal
    from emp e;
    
  • 3、 order by 子句: 排序子句 -- 附加功能: 影响开窗

    -- 按照工资进行排序

    select e.*, sum(sal) over(order by sal)
    from emp e;
    

开窗子句: -- 作用:在分区内进行小范围的划分(开窗),来执行功能函数并复制

  • 1-行偏移开窗,只能向上偏移(必须依赖于order by 子句) rows N preceding -- 作用:在一个分区内,从当前行开始向上偏移几行的小范围划定,再执行功能函数并复制结果 -- N 的范围: 0 和正整数 -- 查询当前行往上偏移两行人员的工资总和
select e.*,sum(sal) over(order by sal rows 2 preceding)
from emp e;

-- 例子: 资产损益表(算出收入和支出的结余工资) /* 名目 收入 支出 结余 上期结余 150000 150000 进货 80000 70000 销售 90000 160000 发工资 60000 100000 */

建表语句

drop table tab;

create table tab(
       prj_date date,
       prj_name varchar2(20),
       in_money number(10,2),
       out_money number(10,2)
);

insert into tab values(to_date('2020-7-1','yyyy-mm-dd'),'上期结余', 150000, null);
insert into tab values(to_date('2020-7-2','yyyy-mm-dd'),'进货', null, 80000);
insert into tab values(to_date('2020-7-3','yyyy-mm-dd'),'销售', 90000, null);
insert into tab values(to_date('2020-7-3 15:16:0','yyyy-mm-dd hh24:mi:ss'),'发工资', null, 60000);
commit;

损益表:使用分析函数

-- 损益表: 使用分析函数
select t.prj_name 名目,t.prj_date 日期,t.in_money 支出,t.out_money 结余,
sum(nvl(in_money,0)) over(order by prj_date rows (select count(1) from tab) preceding) - 
sum(nvl(out_money,0)) over(order by prj_date rows (select count(1) from tab) preceding) 结余
from tab t;

损益表: 不使用分析函数

select t.*, (
       select sum(in_money)
       from (
select t.*,rownum in_rn
from(
select t.prj_date , t.prj_name , nvl(t.in_money,0) in_money , nvl(t.out_money,0) out_money
from tab t
order by prj_date) t) t_in
      where in_rn between 1 and rn
) - 
(
       select sum(out_money)
       from (
select t.*,rownum in_rn
from(
select t.prj_date , t.prj_name , nvl(t.in_money,0) in_money , nvl(t.out_money,0) out_money
from tab t
order by prj_date) t) t_in
      where in_rn between 1 and rn
) 结余
from(
select t.*,rownum rn
from(
select t.prj_date , t.prj_name , t.in_money , t.out_money
from tab t
order by prj_date) t) t;
  • 2-数值偏移开窗 range N preceding -- 从当前行向上计算 小于当前行工资500以内的工资和 -- 从当前数据行向上或者向下查询比当前工资500以内的工资和

    select e.*,sum(sal) over(order by sal range 500 preceding)
    from emp e;
    

-- 求出工资比当前员工高出500以内的人员个数

select e.*,count(1) over(order by sal desc range 500 preceding)-1
from emp e

-- 求出工资比当前员工高出500以上的人员人数

select e.*,
       count(1) over(order by sal desc range (select max(sal) from emp) preceding)-
       count(1) over(order by sal desc range 500 preceding) 
from emp e;

分析函数中常用的功能函数:

  • 1- 分组函数:sum(); avg(); count(); max(); min()

  • 2- 分级函数 rank() -- 注意:需要order by 子句,会产生跳号

    select e.*,rank() over(order by sal)
    from emp e;
    

    -- 求出每个部门工资最高的前三个人的信息

    select t.* 
    from 
    ( select e.*,rank() over(partition by deptno order by sal desc) rk from emp e) t 
    where rk between 1 and 3
    
  • -分级函数 DENSE_RANK: 分级函数 -- 注意: 不会跳号

    select e.*, dense_rank() over(order by sal)
    from emp e
    
  • 3 FIRST_VALUE 、 LAST_VALUE : 取分组中第一个和最后一个值 -- 注意:如果有order by排序,则last_value 不会取指定分区的最后一个,会收到默认数值偏移开窗的影响

    select e.*,
    first_value(sal) over(partition by deptno order by sal),
    last_value(sal) over(partition by deptno)
    from emp e
    
  • 4 LAG(col,N) 向上偏移N行, LEAD(col,N) 向下偏移N行 -- 作用:不使用自连接的情况下,跨行取数据 -- 注意:必须指定 order by 子句 select e.*, lag(sal,2) over(order by empno), lead(sal,3) over(order by empno) from emp e

  • 5、NTILE: 分区片 -- 对所有结果进行分页(对数据分4页,再进行选择第几页)

    select * from (
    select e.*,
    ntile(4) over(order by sal) page
    from emp e )
    where page = 4;
    
  • 6、ROW_NUMBER( ) : 添加行序号 -- 注意:需要order by子句

    使用ROW_NUMBER()函数进行分页

    select * from (
    select e.*,
    row_number() over(order by sal ,deptno desc) rum
    from emp e) 
    where rum between 1 and 5
    

层次化查询

-- 只有层次关系的表才能使用层次化查询 语法:

SELECT [LEVEL] column,……
	FROM tableName
	WHERE clause
	START WITH start_condition
	CONNECT BY PRIOR  prior_condition
  • 查询员工和管理者的层次关系

    select e.*,level
    from emp e
    start with empno = 7369
    connect by prior mgr = empno
    

    查询员工和管理者的层次关系树状显示 lpad代表左填充

    select lpad(ename,level*3+6,' ')
    from emp e
    start with empno = 7839
    connect by prior empno = mgr;
    

层次化查询的意义:

  • 1- 查询子树 -- 查询 7566 JONES 领导的团队

    select lpad(ename,level*3+6,' ')
    from emp e
    start with empno = 7566
    connect by prior empno = mgr;
    
  • 2- 反向遍历 -- 查询 7788 SCOTT 其领导

    select lpad(ename,level*3+6,' ')
    from emp e
    start with empno = 7788
    connect by prior mgr = empno;
    

好了,我们今天就分享Oracle的分析函数和层次化查询,下期我们继续分享Oracle的视图、索引、存储过程和触发器,期待的小伙伴点赞👍+关注👉我叭。