oracle 分析函数之排序与统计

一、分析函数与排序

rank() ,dense_rank(), row_number(). 这些函数都会为每一行生成一个排序的数字,但略有不同,rank()有相同排序的时候,自动去掉下一次的那个数字。比如并列第二名,就没有第三名了。dense_rank()不去掉重复的,row_number()在排序相同的情况下,数字是不一样的。分析函数有点副作用就是会自动排序,看下面并没有order by语句。

SELECT sal,
  rank()over(order by sal),
  dense_rank() over (order by sal),
  row_number() over(order by sal)
FROM emp;

oracle 分析函数之排序与统计

这些排序函数后必须要有order by,不然怎么排序。还可以加partition by用来分组,分组后排序是组内排序,

select empno,deptno,row_number()over(partition by deptno order by empno) from emp;

这个语句按 部门分组,按员工编号排序,输出结果如下
7782    10    1
7839    10    2
7934    10    3
7369    20    1
7566    20    2
7788    20    3
7876    20    4
7902    20    5
7499    30    1
7521    30    2
7654    30    3
7698    30    4

有时候我们想取出中间的几分之几,可以使用ntile() 函数来实现。

select empno,ntile(3)over(order by empno) from emp;

7369    1
7499    1
7521    1
7566    1
7654    1
7698    2
7782    2
7788    2
7839    2
7844    2
7876    3
7900    3
7902    3
7934    3

仔细看,所有数据被分成了3部分,每部分自己标了个数字。但是3只有4个,这是需要注意的地方,几分之几并不是理想的,这只有14条数据,所以第三部分只有4条。函数也可以分组,下面的语句,按部门分组,员工号排序,统计每个部门内的3分之一

select deptno,ntile(3)over(partition by deptno order by empno)from emp;

10    1
10    2
10    3
20    1
20    1
20    2
20    2
20    3
30    1
30    1
30    2
30    2
30    3
30    3


first_value() last_value()这两个函数用来取第一个值和最后一个值。

SELECT empno,
  deptno,
  first_value(empno)over(order by empno),
  last_value(empno)over(order by empno)
FROM emp;

7369    20    7369    7369
7499    30    7369    7499
7521    30    7369    7521
7566    20    7369    7566
7654    30    7369    7654
7698    30    7369    7698
7782    10    7369    7782
7788    20    7369    7788
7839    10    7369    7839
7844    30    7369    7844
7876    20    7369    7876
7900    30    7369    7900
7902    20    7369    7902
7934    10    7369    7934
first_value() 取组内的第一个值,当然是7369了。但last_value()取的并不是组内最后一个值,而是从第一个值到当前这一行的最后一个值。其实写全了应该是last_value(order by empno rows between unbounded preceding and current row). 如果要取组内最后一个值,应该如下写

last_value(xxx) over (partition by xxx order by xxx rows between unbounded prededing and 
unbounded following)

另外一个分析函数,keep,跟上面的函数有点像。keep是取出组内的第一条或者最后一条数据。但是组内的数据dense_rank()之后,可能并列第一或者最后,所以需要用min或者max聚合函数去重。keep的出现主要是解决dense_rank()需要嵌套一层的问题,以前用rank()之后,还要再来一层取rank值为1的,如果用keep的话就只有一层,不用嵌套了。另外,如果我想同时取出组内第一条和最后一条数据,用rank()或者first_value(),last_value()也是不好实现的。有了keep就可以方便的实现一次取出第一条和最后一条。这也正是分析函数的用处,当需要多次扫描同样的数据的时候,就应该考虑分析函数是不是可以用。例子: 取出部门编号,和部门里最老的员工,最新的员工

SELECT deptno,
  MIN(ename)keep(dense_rank FIRST ORDER BY hiredate),
  MIN(ename)keep(dense_rank last ORDER BY hiredate)
FROM emp
GROUP BY deptno;

二、分析函数与统计

    聚合函数sum,avg,min,max,count都可以变成分析函数来使用。统计也分很多种,但其实都是选取不同的范围,给出统计值。
例:取出员工薪水和员工所在部门平均薪水

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

如果加入order by子句,可以实现滚动统计,例:取出员工薪水,所有员工的累计薪水

select empno,sal,
  sum(sal)over(order by empno)
from emp;

7369    800    800
7499    1600    2400
7521    1250    3650
7566    2875    6525
7654    1250    7775
7698    2750    10525
7782    2450    12975
7788    3500    16475
7839    5100    21575
7844    1500    23075
7876    1100    24175
7900    950    25125
7902    3000    28125
7934    1400    29525

如果即加partition by 又加order by ,就是分组累计,下面没给出一个员工薪水,同一部门之前员工平均薪水。

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

有时候,我们不按分组来统计,而是取当前这一行的前几行后几行一起来统计,这时候可以给分析函数加上子句rows between and ....例:取出员工编号和薪水,按入职时间排序该员工一前一后的两人的平均薪水:

select empno,sal,
  avg(sal)over(order by hiredate rows BETWEEN 1 preceding and 1 following)
from emp;

注意:有的数据前面或后面没有数据,会被忽略。

除开rows between ,还可以写range between

select empno,sal,
  avg(sal)over(order by hiredate range between interval '2' year preceding and interval '2' year following)
from emp;

oracle还提供了两个方便的函数lead() lag().一个是向前访问某一行。一个是向后访问某一行。例:取出员工薪水,和按入职时间排序一前一后两个人的薪水:

select empno,sal,
  lead(sal,1)over(order by hiredate desc nulls last),
  lag(sal,1)over(order by hiredate desc nulls last)
from emp;

有的行前面没有人,这样的情况会返回null。

你可能感兴趣的:(oracle 分析函数之排序与统计)