Oracle分析函数

  1. 云栖社区>
  2. 博客>
  3. 正文

Oracle分析函数

技术小胖子 2017-11-09 15:41:00 浏览1043
展开阅读全文

分析函数,通过对SQL语言进行扩展。

例子:将计算scott方案中emp表的员工薪资的运行总计,分部门运行总计。

select ename,deptno,sal,SUM(sal) OVER (order by deptno,ename)运行总计,SUM(sal) OVER(PARTITION by deptno order by ename)分部门运行总计,ROW_NUMBER()OVER(PARTITION by deptno order by ename) 序列  from emp where deptno IN(10,20) order by deptno,ename


1.sum和over是分析函数的语句,sum是一个分析函数,尽管这个函数与分组函数中的sum同名,但是在与over关键字连用时,sum被标识为分析函数。

2.order by 是可选的关键字,有些函数需要,

3.PARTTITION BY 是可选的分区子句

4.row_number()函数根据排序标准,返回每个组的行编号,row_number()返回的行号根据分区与排序的不同而不同。


分析函数结构

一.分析函数名

oracle提供了26个分析函数 分为五大类

1.等级函数:用于前N种查询,比如ROW_NUMBER函数,RANK, DENSE_RANK等

select o.deptno,o.job,sum(o.sal) 部门职级汇总,rank( )over(order by sum(o.sal) desc) 薪资等级, dense_rank() over(order by sum(o.sal)desc)dense_rank  排名,row_number() over (partition by o.deptno order  by sum(o.sal) desc) 分组行号,sum(sum(o.sal)) over  (partition by o.deptno order by o.job)部门薪资运行总计 from emp o where deptno in (10,20,30) and job is not  null group by o.deptno,o.job; 

可以看到rank和dense_rank对记录进行了排名。

2.开窗函数:用来计算行的累计值,这些函数与分组函数同名, 比如sum count avg等

sum(t.sal) over (order by t.deptno,t.ename)  running_total

3.制表函数:与开窗函数相似,制表函数也允许对一个结果集执 行多种聚合运算,比如min,max sum count等,与窗口函数不同 的是,制表函数不能指定一个本地窗口,因此总是在整个分区或 整个组上产生相同的结果

sum(t.sal) over()running_total2

可以看到比开窗函数基础上少了order by子句

4.LAG和LEAD函数:允许在结果集中向前或向后检索值,如果要避 免数据的自连接,它们是非常有用的

5.其他统计函数:例如var_pop  var_samp  和stdev_pop及线性 的衰减函数等这些函数计算任何未排序分区的统计值。

二.分区子句

分区子句使用partition by关键字,用来简单的结果集分为N组。

select deptno,ename,empno row_number () over(partition by deptno order by empno) 分组行号 from emp where deptno in (10,20,30);

当指定了partition by 关键字时 行号在切换到不同的deptno时 将自动从1开始重新分配

三.排序子句

order by 子句用于指定分组中数据的排序方式,排序方式会明显地影响任何分析函数的结果。

分析函数总是按order by对行排序,然而,分析函数中的order by子句只能对各个分组进行排序,而不能保证查询结果有序。要保证最后的查询结果有序,可以使用查询的order by子句

四.开窗子句

开窗子句必须定义在order by子句的后面,用来定义一个变化或固定的数据窗口方法,分析函数将对这些数据进行操作。在分区内部基于任何的变化或固定的窗口中,通过窗口让分析函数来计算其值

例如统计各部门的工资小计及所有部门的薪资总计

select deptno,sum(sal) 部门薪资小计,sum(sum(sal))over(order by deptno rows between unbounded preceding and unbounded following )部门总计 from emp group by deptno;


rows between unbounded preceding and unbounded following这个子句是oracle提供的开窗子句,是指将计算第一条到最后一条的记录,也就是表中所有的记录


分析函数列表

1.COUNT函数

统计各分区中各组的行数

select empno,ename count(*) over (partition by deptno order by empno)条数小记 from emp;

Distinct用来统计唯一值,仅在分区子句中使用,不能在order by 和开窗子句中使用

统计与当前员工工资的差异在50~150之间的其他员工的记录

select empno,ename,sal,count(*)over(order by sal range between 50 preceding and 150 following)薪水差异个数 from emp;

range between来判断sal值是否在50到150之间

2.sum函数

用来汇总分区中的记录

3.avg函数

例如要计算emp表中每个部门的平均薪资表。

select deptno,empno,ename,sal,avg(sal) over (partition by deptno order by deptno)avg_sal from emp;

4.min和max函数

用来计算分区中的最小值或最大值,这两个函数的声明语法

查询同一部门中雇佣日期比当前员工早的员工的最低薪水与最高薪水

select deptno,empno,ename,hiredata,sal,min(sal) over(partition by deptno order by hiredate rang unbounded preceding)最低薪水 max(sal) over(partition by deptno order by hiredate range unbounded preceding )最高薪水 from emp;

5.rank,dense_rank和row_number函数

rank和dense_rank都用来记录编号,根据order by子句中表达式的值,计算它们与其他行的相对位置,每一行赋一个数字序号,形成一个从1开始的序列,将相同的值得到同样的数字序号。不同之处在于rank将相同的行分配同样的序号之后,后面的行将跳跃,比如如果两行序数为1,则没有序数2,直接跳到序数3,而dense_rank则没有任何跳跃值。

row_number也是从1开始用来为记录分配行号,与rank和dense_rank不同的是,不管是否存在重复行,分区类的序列值始终递增。

select deptno,ename,sal,mgr,

rank() over(order by deptno) rank 结果,

dense_rank() over(order by deptno)dense_rank 结果,

row_number()over(order by deptno)row_number 结果 from

emp where deptno in (10,20,30) and mgr is not null order by deptno;


6.FIRST 和LAST 函数

first和last 取dense_rank返回的集合中取出排在第一的行和最后一行。

下面的语句使用first和last返回emp表中具有最高提成的员工薪水和具有最低提成的员工薪水:

select deptno,

min(sal)keep(dense_rank first order by comm)最低提成薪水,

max(sal)keep (dense_rank last order by comm)最高提成薪水

from emp where deptno in(10,20,30) group by deptno;

显示最高提成和最低提成员工的薪资


7.first_value和last_value 函数

分别用来返回over子句中查询出来的第一条记录和最后一条记录。

获取部门中第一条薪资记录值和最后一条记录值

select deptno,empno,sal,

first_value(sal) over (partition by deptno order by empno)"第一个值",

last_value(sal)over(partition by deptno order by empno) "最后一个值"

from emp where deptno in(10,20);


8.lag和lead函数

lag的功能是返回指定列col前n1行的值(如果前n1行已经超出比照范围,则返回n2,如果不指定n2则默认返回null),如果不指定n1,其默认值为1;lead函数与此相反,返回指定列coll后面的n1行的值。

使用lag和lead函数查找当前雇佣的前一个雇员的薪水和后一个雇员的薪水

select ename,hiredate,sal,deptno,

lag(sal,1,0)over(order by hiredate)as 前一个雇员薪水,

lead(sal,1,0)over(order by hiredate)as 后一个雇员薪水 

from emp where deptno=30;

可以看到由于第一条记录的前一条不存在,因此前一个雇员的薪水返回0;最后一条记录由于不存在后一条记录,因此后一个雇员的薪水也为0


分析函数使用示例

1.记录排名

人事部需要对emp表中各个不同部门的员工薪资总计进行排名,可以使用如下:

select deptno ,empno,sum(sal) dept_sales,

rank()over(partition by deptno order by sum(sal)

dese nulls last)薪资排名_跳号,

dense_rank()over(partition by deptno order by sum(sal )desc nulls last)薪资排名_同级同号,

row_number()over(partition by deptno order by sum(sal )desc nulls last)薪资排名_不跳号

from emp group by deptno,empno,ename;

语句中使用nulls last  使得null值排在分区的最后。


2.首尾记录查询

查询出员工薪资中最高和最低薪资的员工编号,听起来需要两个分组查询,实际上使用分组函数后,一行语句就可以得到想要的结果。

select min(empno)keep(dense_rank first order by sum(sal)desc   nulls last)薪资排名首位,

       min(empno)keep(dense_rank last order by sum(sal) desc  nulls last) 薪资排名尾位

from emp where sal is not null and deptno is not null group by empno;

keep用于告诉oracle保存符合keep后面语句条件的记录。


3.前后排名查询

查询部门的员工薪资排名在前2位的员工列表

select deptno,empno,ename,dept_sales,薪资排名 from

select deptno,empno,ename,sum(sal)dept_salse,

rank()over(partition by deptno order by sum(sal)

   desc nulls last)薪资排名

from emp where deptno is not null group by deptno,empno,ename— )where 薪资排名<=2;

上述查询结果作为一个内联视图,通过对内联视图返回的排名进行排序,很轻松就得到了部门分组中的薪资的前2名。当然如果要获取排名在后2位的员工,只需要order by 子句中将排序进行反转,按正向进行排序即可

select deptno,empno,ename,dept_sales,薪资排名 from

select deptno,empno,ename,sum(sal)dept_sales,

rank()over(aprtition by deptno order by sum(sal)  nulls last)薪资排名

from emp where deptno is not null group by deptno,empno,ename)

where 薪资排名<=2;

4.层次查询

所为查询,是指按比率得到结果数,比如想查询薪资排名在前1/3的员工列表。 要查询薪资在前1/3的员工列表

select * from

select deptno,empno,ename,sum(sal)dept_sales,

NTILE(3)over(partition by deptno 

order by sum(sal)nulls last)rank_ratio

from emp where deptno is not null 

group by deptno,empno,ename)

where rank ratio=1;

5。范围统计查询

范围查询是分析函数发挥其功能的重点,范围查询是指查询当前记录的前面或后面的记录进行统计。

比如想知道员工史密斯的雇佣日期前10天和后10天新进员工的最高薪资。

select empno,ename,hiredate,sal,

MAX(sal)over(order by hiredate rows between 10 preceding and

current row) "前10天入职员工最高薪资",

MAX(sal)over(order by hiredate rows between current row and 10

following)"后10天入职员工最高薪资"

from emp

where deptno in(10,20,30)and sal is not null;

6.相邻记录比较

查询当前员工前一个入职的员工和后一个入职的员工与当前员工的薪资差异

select ename,hiredate,deptno,sal,sal-prev_sal "与前面的差异" ,sal-next_sal "与后面的差异"

from (select ename,hiredate,sal,deptno,

LAG (sal,1,0)over(order by hiredate)as prev_sal,

LEAD(sal,1,0)over(order by hiredate)as next_sal

from emp

where deptno is not null and sal is not null);


7.抑制重复

人事部需要随机地提取各部门在1980 1981 和1982年入职的任意一个员工,由于在各个部门中这几个年份入职的员工较多,因此人事部门只是随意地提取一个员工即可,不要求统计提取,可以使用如下的分析查询语句:

select * 

from (select empno,ename,sal,hiredate,

ROW_NUMBER()over(partition by extract

(year from hiredate) order by empno) rn

from emp

where hiredate is not null

and extract (year from hiredate) in (1981,1982,1983))

where rn=1;

8.行列转换查询

select job,empno,ename

row_number()over(partition by job order by ename)rn

from emp

where job is not null;



select max(decode(job,'职员',ename,NULL))"职员名称",

    max(decode(job,'分析人员',ename,NULL))"分析人员名称",

    max(decode(job,'经理',ename,NULL))"经历名称",

      max(decode(job,'老板',ename,NULL))"老板名称",

    max(decode(job,'销售人员',ename,NULL))"销售人员名称"

from(select job,empno,ename,row_number()over (partition by  job order by ename)rn from emp where job is not null)x

在PL/SQL中使用分析函数

declare

type refempcur is ref cursor;

empcur refempcur;

jobname varchar(20);

ename   varchar2(20);

empno   number;

rn int;

begin

open empcur for

select job,empno,ename,row_number () over (partition by job  order by ename)rn from emp where job is not null;

loop

exit when empcur%NOTFOUND;

fetch empcur

into jobname,empno,ename,rn;

dbms_output.put_line(jobname || '  '||empno || '  '||ename||'   '||rn);

end loop;

end;



      本文转自潘阔 51CTO博客,原文链接:http://blog.51cto.com/pankuo/1630251,如需转载请自行联系原作者






网友评论

登录后评论
0/500
评论
技术小胖子
+ 关注