一、
情景说明:入职期间做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种查询
开窗(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)
引用文章:
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
运行结果如下:
引用文章: