zoukankan      html  css  js  c++  java
  • Oracle中的分析函数与窗口函数

    一、默认窗口与排名函数

    最常用的分析函数是排名函数,用于返回当前记录在窗口中的位置。常用的排名函数包括rank()、dense_rank()、row_number()等。排名函数所对应的窗口函数必须指定排序规则,即必须使用order by 子句,而Oracle为每条记录提供的默认窗口为从表中第一条记录开始,直至与当前记录具有相同排名的所有记录。

    1、rank()函数

    将表中员工按入职时间升序排列,列出排名次序 
    select empno, ename, hiredate, rank() over(order by hiredate)  hiredate_order from emp;

    该函数的排名具有跳跃性,如7900和7902员工排名都是10,而下一位7934员工排名则为12。

    2、dense_rank()函数

    类似rank()函数,但排名不具有跳跃性 
    select empno, ename, hiredate, dense_rank() over(order by hiredate) hiredate_order from emp;

    7900和7902员工排名都是10,而下一位7934员工排名则为11,排名不存在跳跃。

    3、row_number()函数

    row_number()函数单纯返回当前记录在窗口中所处位置的行号,该行号具有唯一性。以上查询用row_number()函数替代后如下
    select empno, ename, hiredate, row_number() over(order by hiredate) hiredate_order from emp;

    4、count()函数

    通过count()函数可以查看窗口中实际的记录数目,以上查询用count()函数替代后如下 
    select empno, ename, hiredate, count(1) over(order by hiredate) hiredate_order from emp;

    二、分区窗口

    分区窗口是与当前记录拥有相同的分区标准的所有记录,语法为 
    partition by 列名 
    partition by语句首先根据列名获得当前记录的列值,接着获得表中具有相同列值的所有记录,并将该记录集合作为当前记录的窗口。 
    需要注意的是,当在分区窗口中使用order by排序后,窗口返回的不再是全部记录,而是只到当前记录,从而成为默认大小的窗口。

    示例: 
    查询EMP表每个员工的职位和薪水,并给出该职位员工的平均薪水 
    select empno, ename, job, sal, round(avg(sal) over(partition by job)) avg_sal from emp;

    示例: 
    在查询每个员工职位和入职时间的同时,获得其在该职位中按入职时间进行的排序位置 
    select empno, ename, job, hiredate, dense_rank() over(partition by job order by hiredate) hiredata_order from emp;

    三、窗口子句

    窗口子句可以进一步限制或扩展窗口的范围。

    1、rows子句

    rows子句的使用前提为窗口已经利用order by 进行了排序。此时可以利用rows子句按照位置向前或向后追溯,从而进一步调整窗口大小。语法如下 
    over(order by 列名 rows between 位移量 preceding and 位移量 following)

    preceding向前追溯处于当前记录之前的记录,following向后追溯处于当前记录之后的记录。

    示例: 
    在查询每个员工入职时间和薪水的同时,计算与其前后相邻入职时间的共三位员工的平均薪水 
    select empno, ename, job, hiredate, sal, round(avg(sal) over(order by hiredate rows between 1 preceding and 1 following)) avg_sal from emp;

    对于第一条记录和最后一条记录来说,实际上对应的窗口只有两条记录。

    2、range子句

    rows子句以相对位置作为获取记录的标准,而range子句则以相对列值作为筛选记录的标准。语法如下 
    over(order by 列名 range between 差值 preceding and 差值 following)

    示例: 
    在查询每个员工入职时间的同时,计算包括与其入职时间前后相差一年之内的员工数量 
    select empno, ename, job, hiredate, count(1) over(order by hiredate range between 365 preceding and 365 following) count_emp from emp;

    3、current row与unbounded

    在窗口子句中,除了可以用确定的数值来限定窗口外,还可以使用current row来指定当前记录,使用unbounded来代替数值,表示不受限制的窗口范围。

    向前位移无限制 
    select empno, ename, job, hiredate, sal, round(avg(sal) over(order by hiredate rows between unbounded preceding and current row)) avg_sal from emp;

    向后位移无限制 
    select empno, ename, job, hiredate, sal, round(avg(sal) over(order by hiredate rows between 1 preceding and unbounded following)) avg_sal from emp;

    前后位移均无限制 
    select empno, ename, job, hiredate, sal, round(avg(sal) over(order by hiredate rows between unbounded preceding and unbounded following)) avg_sal from emp;

    四、常用分析函数

    很多聚合函数均可以和窗口函数结合使用,如max()、min()、sum()等。除此之外,Oracle还提供了另外几种常用的分析函数。

    1、first_value()函数

    first_value()函数返回已排序窗口中第一条记录的相关信息。

    示例: 
    在查询员工入职时间和所在部门的同时,指出该部门入职时间最早的员工 
    select empno, ename, hiredate, deptno, first_value(empno) over(partition by deptno order by hiredate) first_emp from emp;

    2、last_value()函数

    与first_value()函数相反,last_value()函数返回分区中最后一条记录的相关信息。

    示例: 
    在查询员工入职时间和所在部门的同时,指出该部门入职时间最迟的员工。首先说明,套用fist_value()的查询写法是不行的,如下 
    select empno, ename, hiredate, deptno, last_value(empno) over(partition by deptno order by hiredate) last_emp from emp;

    没有达到希望的查询结果,原因正如前面所说,当在分区窗口中使用order by排序后,窗口返回的不再是全部记录,而是只到当前记录,从而成为默认大小的窗口。正确的做法是可以利用rows子句来指定无限制的窗口,如下 
    select empno, ename, hiredate, deptno, last_value(empno) over(partition by deptno order by hiredate rows between unbounded preceding and unbounded following) last_emp from emp;

    由此可见,last_value()函数往往需要结合使用rows子句使用,因此,使用first_value()函数要优于使用last_value()函数。其实为了获得与last_value()相同的效果,使用first_value()函数时,只要为order by子句添加desc进行降序排列即可。

    3、lag()函数

    first_value()和last_value()可以返回窗口中第一条和最后一条记录,但不能处理其他记录。为此,Oracle提供了lag()函数和lead()函数来灵活处理其他记录。

    lag()函数以当前记录为坐标,按照指定的位移量向上搜索,并尝试捕获记录,捕获不到时返回指定的默认值,语法如下 
    lag(列名或列表达式, 位移量, 默认值)

    这里要求默认值的数据类型应当与列名或列表达式的类型兼容。

    示例: 
    查询每个员工的工资信息,并给出工资排名在他前一位的员工 
    select empno, ename, sal, lag(empno, 1, null) over(order by sal) lag_empno, lag(ename, 1, 'N/A') over(order by sal) lag_ename from emp;

    4、lead()函数

    该函数与lag()函数类似,不同之处是lead()函数的位移量为向下偏移。

    示例: 
    查询每个员工的工资信息,并给出工资排名在他后一位的员工,这里直接用lead()替换lag()即可 
    select empno, ename, sal, lead(empno, 1, null) over(order by sal) lag_empno, lead(ename, 1, 'N/A') over(order by sal) lag_ename from emp;

    这里发现,当使用lead()函数做位移推进时,over(order by)这样原本只到当前记录的默认大小的窗口实际范围变大了,从而能够获取到往下的记录。

    5、ratio_to_report()占比函数

    查询每个员工的工资及其在所有员工和所在部门员工中的百分比 
    select empno, ename, sal, round((ratio_to_report(sal) over()) * 100, 2) pct1, round((ratio_to_report(sal) over(partition by deptno)) * 100, 2) pct2 from scott.emp;

  • 相关阅读:
    AirtestIDE这个隐藏的小助手,还没用过你就亏啦!
    不懂抓包也能做APP爬虫?1招教你爬取抖音流行歌名
    巧用bat文件做Airtest脚本的“批量运行”
    AirtestIDE1.2.4、1.2.5新版来袭!新增自动更新、iOS更多版本支持...
    NodeService Ensure that Node.js is installed and can be found in one of the PATH directories
    NET CORE通过NodeService调用js
    Java8 的 Stream API 的确牛X,但性能究竟如何呢?
    Java 生成二维码实战
    Java高并发之设计模式,设计思想
    NullPointerException 的处理新方式,Java14 真的太香了!
  • 原文地址:https://www.cnblogs.com/mxh168/p/13965341.html
Copyright © 2011-2022 走看看