您的位置:首页 > 数据库 > Oracle

超级牛皮的ORACLE特有函数 over(...),lag与lead函数--工作备忘2016/1/14

2016-01-15 17:10 393 查看
一、

情景说明:入职期间做SQL语句逻辑迁移工作,遇到一种情况,一个活动分为很多阶段,并且有一个当前阶段值,阶段关联一个workflow过程

需要显示活动实例的每一阶段并附上当前阶段的workflow过程

如:(理想效果)



之前却只能实现如下方式(错误结果)



之前实现理想效果只能使用同一张表多次查询的方法,但性能不用需

之后使用  MAX(case when CSTG.id= CA.id_actual_STAGE then  WF.NAME end)
  over(PARTITION BY CA.ID ) --over()分析函数 配合 PARTITION BY 分组条件完成

同表自连接查询

over()分析函数说明:

--PARTITION BY 分组条件

-- order by 排序条件 

--窗口函数  range between XX preceding and XX following 和 rows
between XX preceding and XX following 

分析函数语法:

FUNCTION_NAME(<argument>,<argument>...)   --比如sum,max,rank .......常用函数

OVER

(<Partition-Clause><Order-by-Clause><Windowing Clause>)

例:

sum(sal) over (partition by deptno order by ename) new_alias

sum就是函数名

(sal)是分析函数的参数,每个函数有0~3个参数,参数可以是表达式,例如:sum(sal+comm)

over 是一个关键字,用于标识分析函数,否则查询分析器不能区别sum()聚集函数和sum()分析函数

partition by deptno 是可选的分区子句,如果不存在任何分区子句,则全部的结果集可看作一个单一的大区

order by ename 是可选的order by 子句,有些函数需要它,有些则不需要.依靠已排序数据的那些函数,如:用于访问结果集中前一行和后一行的LAG和LEAD,必须使用,其它函数,如AVG,则不需要.在使用了任何排序的开窗函数时,该子句是强制性的,它指定了在计算分析函数时一组内的数据是如何排序的.

1)FUNCTION子句
ORACLE提供了26个分析函数,按功能分5类
分析函数分类
等级(ranking)函数:用于寻找前N种查询
<
1b30c
/span>开窗(windowing)函数:用于计算不同的累计,如SUM,COUNT,AVG,MIN,MAX等,作用于数据的一个窗口上
例:

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

sum(t.sal) over (partition by t.deptno order by t.ename) department_total
制表(reporting)函数:与开窗函数同名,作用于一个分区或一组上的所有列
例:

sum(t.sal) over () running_total2,

sum(t.sal) over (partition by t.deptno ) department_total2
制表函数与开窗函数的关键不同之处在于OVER语句上缺少一个ORDER
BY子句!

LAG,LEAD函数:这类函数允许在结果集中向前或向后检索值,为了避免数据的自连接,它们是非常用用的.

VAR_POP,VAR_SAMP,STDEV_POPE及线性的衰减函数:计算任何未排序分区的统计值

2)PARTITION子句
按照表达式分区(就是分组),如果省略了分区子句,则全部的结果集被看作是一个单一的组

3)ORDER BY子句
分析函数中ORDER BY的存在将添加一个默认的开窗子句,这意味着计算中所使用的行的集合是当前分区中当前行和前面所有行,没有ORDER
BY时,默认的窗口是全部的分区 在Order
by 子句后可以添加nulls last,如:order
by comm desc nulls last   表示排序时忽略comm列为空的行.   

窗口子句中我们经常用到指定第一行,当前行,最后一行这样的三个属性

第一行是 unbounded preceding,
当前行是 current row,
最后一行是 unbounded following,
窗口子句不能单独出现,必须有order by子句时才能出现,

a)
如果存在order by则默认的窗口是unbounded preceding and current row  --当前组的第一行到当前行

b) 如果同时省略order by则默认的窗口是unbounded preceding and unbounded following  --整个组

4)WINDOWING子句
用于定义分析函数将在其上操作的行的集合

Windowing子句给出了一个定义变化或固定的数据窗口的方法,分析函数将对这些数据进行操作
默认的窗口是一个固定的窗口,仅仅在一组的第一行开始,一直继续到当前行,要使用窗口,必须使用ORDER
BY子句
根据2个标准可以建立窗口:数据值的范围(RANGES)或与当前行的行偏移量.

4.1)Rang窗口

Range 5 preceding:将产生一个滑动窗口,他在组中拥有当前行以前5行的集合

ANGE窗口仅对NUMBERS和DATES起作用,因为不可能从VARCHAR2中增加或减去N个单元

另外的限制是ORDER BY中只能有一列,因而范围实际上是一维的,不能在N维空间中

例:

avg(t.sal) over(order by t.hiredate asc range 100 preceding) 

avg(t.sal) over(order by t.hiredate asc range 100 preceding) 统计前100天平均工资

4.2)Row窗口

利用ROW分区,就没有RANGE分区那样的限制了,数据可以是任何类型,且ORDER
BY 可以包括很多列

4.3)Specifying窗口

UNBOUNDED PRECEDING:这个窗口从当前分区的每一行开始,并结束于正在处理的当前行

CURRENT ROW:该窗口从当前行开始(并结束)

Numeric Expression PRECEDING:

Numeric Expression PRECEDING:对该窗口从当前行之前的数字表达式(Numeric
Expression)的行开始,对RANGE来说,从从行序值小于数字表达式的当前行的值开始.

Numeric Expression FOLLOWING:

Numeric Expression FOLLOWING:该窗口在当前行Numeric Expression行之后的行终止(或开始),且从行序值大于当前行Numeric
Expression行的范围开始(或终止)

range between 100 preceding and 100 following:

range between 100 preceding and 100 following:当前行100前,当前后100后

注意:分析函数允许你对一个数据集进排序和筛选,这是SQL从来不能实现的.除了最后的Order
by子句之外,分析函数是在查询中执行的最后的操作集,这样的话,就不能直接在谓词中使用分析函数,即不能在上面使用where或having子句!!!




窗口函数进阶--滚动统计(累积/均值)

    考虑前面提到的第2个需求:列出每月的订单总额以及截至到当前月的订单总额。也就是说2月份的记录要显示当月的订单总额和1,2月份订单总额的和。3月份要显示当月的订单总额和1,2,3月份订单总额的和,依此类推。

    很明显这个需求需要在统计第N月的订单总额时,还要再统计这N个月来的订单总额之和。想想上面的语句,假如我们能够把and unbounded following换成代表当前月份的逻辑多好啊!很幸运的是Oracle考虑到了我们这个需求,为此我们只需要将语句稍微改成: current
row就可以了。

SQL> select month,

            sum(total_sales) month_sales,

            sum(sum(total_sales)) over (order by month

                    rows between unbounded preceding and current row ) total_sale

        from win_order group by month;   

MONTH MONTH_SALES TOTAL_SALE

----- ----------- ----------

    1      623141    623141

    2      423124    1046265

    3      323214    1369479

    4      212314    1581793

    5      654314    2236107

    6      122134    2358241

    7      859234    3217475

    8      752314    3969789

    9      365314    4335103

  10      265314    4600417

  11      563114    5163531

  12      595314    5758845

 

12 rows selected

下面的结果一样:

SQL> select month,

  2  sum(total_sales) month_sales,

  3  sum(sum(total_sales)) over (order by month) total_sale

  4  from win_order group by month

  5  ;

 

MONTH MONTH_SALES TOTAL_SALE

----- ----------- ----------

    1      623141    623141

    2      423124    1046265

    3      323214    1369479

    4      212314    1581793

    5      654314    2236107

    6      122134    2358241

    7      859234    3217475

    8      752314    3969789

    9      365314    4335103

  10      265314    4600417

  11      563114    5163531

  12      595314    5758845

 

12 rows selected

因此,rows between unbounded preceding and current row 为窗口默认值!

  现在我们能得到滚动的销售总额了!下面这个统计结果看起来更加完美,它展现了所有我们需要的数据

SQL> select month,

            sum(total_sales) month_sales,

            sum(sum(total_sales)) over(order by month

                rows between unbounded preceding and current row) total_sales,

            sum(sum(total_sales)) over(order by month

            rows between unbounded preceding and unbounded following) total_sales

        from win_order group by month

  8  ;

 

MONTH MONTH_SALES TOTAL_SALES TOTAL_SALES

----- ----------- ----------- -----------

    1      623141      623141    5758845

    2      423124    1046265    5758845

    3      323214    1369479    5758845

    4      212314    1581793    5758845

    5      654314    2236107    5758845

    6      122134    2358241    5758845

    7      859234    3217475    5758845

    8      752314    3969789    5758845

    9      365314    4335103    5758845

  10      265314    4600417    5758845

  11      563114    5163531    5758845

  12      595314    5758845    5758845

 

12 rows selected

在一些销售报表中我们会时常看到求平均值的需求,有时可能是针对全年的数据求平均值,有时会是针对截至到当前的所有数据求平均值。很简单,只需要将:

sum(sum(tot_sales))换成avg(sum(tot_sales))即可。

SQL> select month,

  2        avg(total_sales) month_avg_sales,

  3        avg(avg(total_sales)) over(order by month

  4        rows between unbounded preceding and current row) current_avg_sales,

  5        avg(avg(total_sales)) over(order by month

  6        rows between unbounded preceding and unbounded following) avg_sale

  7  from win_order

  8  group by month

  9  ;

 

MONTH MONTH_AVG_SALES CURRENT_AVG_SALES  AVG_SALE

----- --------------- ----------------- ----------

    1          623141            623141      479903.75

    2          423124          523132.5    479903.75

    3          323214            456493      479903.75

    4          212314        395448.25    479903.75

    5          654314          447221.4      479903.75

    6          122134  393040.166666667  479903.75

    7          859234  459639.285714286  479903.75

    8          752314        496223.625      479903.75

    9          365314  481678.111111111  479903.75

  10          265314          460041.7        479903.75

  11          563114  469411.909090909  479903.75

  12          595314        479903.75      479903.75

常见函数配合 OVER()使用案例&说明:

1)ORDER BY 排序理解

例1:关注点:sql无排序,over()排序子句省略  

SELECT DEPTNO, EMPNO, ENAME, SAL,
LAST_VALUE(SAL) OVER(PARTITION BY DEPTNO)
FROM EMP;


运行结果:说明:因为该语句没有进行排序,所以最后一个值为默认顺序

窗口按unbounded preceding and unbounded following
当前组进行分析取值

 



        

例2:关注点:sql无排序,over()排序子句有,窗口省略

SELECT DEPTNO,
EMPNO,
ENAME,
SAL,
LAST_VALUE(SAL) OVER(PARTITION BY DEPTNO
ORDER BY SAL DESC)
FROM EMP;


运行结果:

说明:因为该语句按照SAL排序降序(大至小),但没有指定窗口范围,窗口按unbounded preceding and current
row 取值

所以分析函数最后取值为排序之后第一行值当前行的数值



例3:关注点:sql无排序,over()排序子句有,窗口也有,窗口特意强调全组数据

SELECT DEPTNO,
EMPNO,
ENAME,
SAL,
LAST_VALUE(SAL)
OVER(PARTITION BY DEPTNO
ORDER BY SAL
ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) MAX_SAL
FROM EMP;


运行结果:

说明:因为该语句按照SAL排序升序(小直达),但没有指定窗口范围,窗口按unbounded preceding and unbounded
following

所以分析函数最后取值为排序之后当前组的最后一个值



2)

常用的分析函数如下所列:

row_number() over(partition by ... order by ...)

rank() over(partition by ... order by ...)

dense_rank() over(partition by ... order by ...)

count() over(partition by ... order by ...)

max() over(partition by ... order by ...)

min() over(partition by ... order by ...)

sum() over(partition by ... order by ...)

avg() over(partition by ... order by ...)

first_value() over(partition by ... order by ...)

last_value() over(partition by ... order by ...)

lag() over(partition by ... order by ...)

lead() over(partition by ... order by ...)

3)sum()结合over()使用说明

  Sql代码

  select a.empno as 员工编号

  ,a.ename as 员工姓名

  ,a.deptno as 部门编号

  ,a.sal as 薪酬

  ,sum(sal) over (partition by deptno) 按部门求薪酬总和

  from scott.emp a;

  此段sql 执行的结果为:

  部门编号 员工姓名 员工编码 薪酬 按部门求薪酬总和

  7934 MILLER 10 1300 8750

  7782 CLARK 10 2450 8750

  7839 KING 10 5000 8750

  7369 SMITH 20 800 10875

  7876 ADAMS 20 1100 10875

  7566 JONES 20 2975 10875

  7788 SCOTT 20 3000 10875

  7902 FORD 20 3000 10875

  7900 JAMES 30 950 9400

  7654 MARTIN 30 1250 9400

  7521 WARD 30 1250 9400

  7844 TURNER 30 1500 9400

  7499 ALLEN 30 1600 9400

  7698 BLAKE 30 2850 9400

  可以从结果上看到sum()函数对部门区分进行了求和统计。其中“partition by”官方点的说法叫做"分区",其实就是统计的范围条件。

  下面在给把上面的sql语句改造下 给 over() 函数加上 “order by sal” 会看到一个更过瘾的效果:

  Sql代码

  select a.empno as 员工编号

  ,a.ename as 员工姓名

  ,a.deptno as 部门编号

  ,a.sal as 薪酬

  ,sum(sal) over (partition by deptno) 按部门求薪酬总和

  ,sum(sal) over (partition by deptno order by sal) 按部门累计薪酬

  from scott.emp a;

  结果为:

  部门编号 员工姓名 员工编码 薪酬 按部门求薪酬总和  按部门累计薪酬

  7934 MILLER 10 1300 8750 1300

  7782 CLARK 10 2450 8750 3750

  7839 KING 10 5000 8750 8750

  7369 SMITH 20 800 10875 800

  7876 ADAMS 20 1100 10875 1900

  7566 JONES 20 2975 10875 4875

  7788 SCOTT 20 3000 10875 10875

  7902 FORD 20 3000 10875 10875

  7900 JAMES 30 950 9400 950

  7654 MARTIN 30 1250 9400 3450

  7521 WARD 30 1250 9400 3450

  7844 TURNER 30 1500 9400 4950

  7499 ALLEN 30 1600 9400 6550

  7698 BLAKE 30 2850 9400 9400

  从结果中可以看的 加了 “order by”后对 统计进行一个累加,这里个人理解为对统计范围规定了个统计顺利,一步一步的统计。

4)

1、和统计函数(聚合函数)使用

可以和count() 、sum() 、avg() 、min() 、max()这些函数使用,这样在查询市,还可以显示每条记录的信息,比如说查询每个系的老师的平均工资和老师的信息,如图所示:



 

2、和排名函数使用

有时在后台查询数据库时,需要向前台那样显示序号,oracle提供了三个函数,搭配over()函数更有用。如图所示:



 

3、和错行的函数使用

Oracle中有两个类似于错行的行数,在本行可以取到其他行的数据。lead 函数和lag函数。如图所示:



 

4、取最值的函数first_value()

通过和该函数的联合使用,可以解决最值问题。常用的有两外的属性值。



 

无论是那种方式都会在查询的记录上添加一列的信息,有时候需要这种需求,使用这个函数还可以解决使用group by时,只能查询分组的字段值的问题。

引用文章:
http://www.cnblogs.com/wuyisky/archive/2010/02/24/oracle_over.html--OVER()说明 http://www.cnblogs.com/linjiqin/archive/2012/04/05/2433633.html  -- order by 的排序理解
http://www.cnblogs.com/sumsen/archive/2012/05/30/2525800.html  --示例
http://blog.csdn.net/an342647823/article/details/9039339   --其他over() 使用案例 (主要是合并同一类的多个值)
http://www.linuxidc.com/Linux/2013-01/78294p3.htm    --窗口进阶

二、

lag与lead函数是跟偏移量相关的两个分析函数,通过这两个函数可以在一次查询中取出同一字段的前N行的数据(lag)和后N行的数据(lead)作为独立的列,从而更方便地进行进行数据过滤。这种操作可以代替表的自联接,并且LAG和LEAD有更高的效率。

over()表示 lag()与lead()操作的数据都在over()的范围内,他里面可以使用partition by 语句(用于分组) order by 语句(用于排序)。partition by a order by b表示以a字段进行分组,再 以b字段进行排序,对数据进行查询。

例如:lead(field, num, defaultvalue) field需要查找的字段,num往后查找的num行的数据,defaultvalue没有符合条件的默认值。

二、示例

1、表机构与初始化数据如下

-- 表结构

create table tb_test(

  id varchar2(64) not null,

  cphm varchar2(10) not null,

  create_date date not null, 

  primary key (id)

)

表初始化数据为:

  


  2、示例

  a、获取当前记录的id,以及下一条记录的id  

select t.id id ,
lead(t.id, 1, null) over (order by t.id)  next_record_id, t.cphm
from tb_test t
order by t.id asc


  运行结果如下:

  


  b、获取当前记录的id,以及上一条记录的id

select t.id id ,
lag(t.id, 1, null) over (order by t.id)  next_record_id, t.cphm
from tb_test t
order by t.id asc


  运行结果如下:

  


  c、获取号牌号码相同的,当前记录的id与,下一条记录的id(使用partition by)

select t.id id,
lead(t.id, 1, null) over(partition by cphm order by t.id) next_same_cphm_id, t.cphm
from tb_test t
order by t.id asc


  运行结果如下:

  


引用文章:
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息