Oracle-分析函数_总结

一、分析函数是什么?

分析函数是Oracle专门用于解决复杂报表统计需求的功能强大的函数,它可以在数据中进行分组然后基于组计算某种统计值,并且每一组的每一行都可以返回一个统计值。

说白了,分析函数就是 over([query_partition_clase] order_by_clause)。比如说,我采用sum求和,rank排序等等,根据什么来呢?over提供一个窗口,使用partition by进行分组,在组内使用order by进行排序。over不能单独使用,要和分析函数:rank(),dense_rank(),row_number()等一起使用

二、Oracle分析函数与聚合函数的区别:

分析函数用于计算基于组的某种聚合值,它和聚合函数的不同之处是对于每个组返回多行,而聚合函数对于每个组只返回一行。

三、分析函数:

用于合计的函数:

  • sum()函数;

  • rollup()函数;

  • cube()函数;

  • grouping()函数;

  • max() over;

  • min() over;

  • avg() over

用于排列的函数:

  • rank() over 函数;

  • dense_rank() over 函数;

  • row_number() over 函数;

其他:

  • lag() over;

  • lead() over


1. sum()函数:

许多分析函数同时也是聚合函数,比如sum()函数,下面这样使用就是聚合函数。
--按照月份,统计每个地区的总收入

SELECT earnmonth 月份,area 地区,SUM(personincome) 总收入 FROM earnings GROUP BY earnmonth,area;

而这样使用就是分析函数:

SELECT DISTINCT earnmonth 月份,area 地区,

       sum(personincome) OVER (PARTITION BY earnmonth,area) 总收入

       FROM earnings;

它们得出的结果是相同的,都是:

image.png

请注意,这里我们用到了distinct 关键字,如果不用distinct,第2个查询将返回20行数据,即earnings表的每行记录都将返回一行总收入,因为不用distinct的含义是:针对每个打工者计算他/她所在的月份和地区的总收入。

SELECT earnmonth 月份,area 地区,

       sum(personincome) OVER (PARTITION BY earnmonth,area) 总收入

       FROM earnings;
image.png

在这个例子中,聚合函数是更好的选择,但在另外一些情形下,我们更应该使用分析函数。下面通过几个实例来介绍排序分析函数的用途。

问题:统计每个月份,不同地区工资最高的前3名。

2. rank()函数

    语法:rank() over([query_partition_clause]order_by_clause)

利用我们传统的聚合函数max可以方便地取出工资最高的一个员工,但是取出多个就无能为力了,同样,如果不分组我们可以通过排序取出工资最高的前3名,但无法实现对多个月份和地区的分组。而采用rank()分析函数,可以方便地实现我们的要求。

完整的语句如下:

SELECT t.earnmonth 月份,t.area 地区,t.sname 打工者姓名,t.personincome 收入,t.run 排名
FROM (
  SELECT earnmonth,area,sname,personincome,
  rank() OVER(PARTITION BY earnmonth,area ORDER BY personincome desc) run
  FROM earnings
) t WHERE t.run<=3;

结果为:

image.png

我们在开窗函数over()中使用earnmonth(月份)和area(地区)作为分组标志,并按照personincome(收入)倒序排列。

注意:RANK()函数有3组,分别是rank(), dense_rank(), row_number(),它们的区别是:

RANK()如果出现两个相同的数据,那么后面的数据就会直接跳过这个排名,比如:当第2名和第3名的利润相同时,rank的结果是1,2,2,4;而dense_rank()则不会跳过这个排名,结果是1,2,2,3;而row_number()哪怕是两个数据完全相同,排名也会不一样,结果是1,2,3,4.

3. dense_rank()

    语法:  dense_rank() over([query_partition_clause] order_by_clause)

完整的语句如下:

SELECT t.earnmonth 月份,t.area 地区,t.sname 打工者姓名,t.personincome 收入,t.run 排名
FROM (
  SELECT earnmonth,area,sname,personincome,
  dense_rank() OVER(PARTITION BY earnmonth,area ORDER BY personincome desc) run
  FROM earnings
) t WHERE t.run<=3;

结果为:

image.png

4. row_number()

    语法:row_number() over([query_partition_clause]order_by_clause)

完整的语句如下:

SELECT t.earnmonth 月份,t.area 地区,t.sname 打工者姓名,t.personincome 收入,t.run 排名
FROM (
  SELECT earnmonth,area,sname,personincome,
  row_number() OVER(PARTITION BY earnmonth,area ORDER BY personincome desc) run
  FROM earnings
) t WHERE t.run<=3;

结果为:

image.png

5. rollup()函数:

按照月份,地区统计收入
--rollup函数:(分组统计之后,再按照月份做一个汇总)

--按照月份,统计每个地区的总收入
SELECT earnmonth,area,SUM(personincome) FROM earnings GROUP BY ROLLUP(earnmonth,area);

结果为:

image.png

6. cube()函数:

按照月份,地区进行收入总汇总
--cube函数:(分组统计之后,按照月份做一个汇总,再按照地区做一个汇总,最后再来一个收入的总汇总)

SELECT earnmonth,area,sum(personincome)
FROM earnings
GROUP BY cube(earnmonth,area) ORDER by earnmonth,area NULLS last;

结果为:

image.png

7. grouping()函数:

在以上例子中,是用rollup()和cube()函数都会对结果集产生null,这时候可用grouping函数来确认该记录是由哪个字段得出来的.

grouping函数用法,带一个参数,参数为字段名,如果当前行是由rollup或者cube汇总得来的,结果就返回1,反之返回0.

完整语句如下:

SELECT decode(grouping(earnmonth),1,'所有月份',earnmonth) 月份,
decode(grouping(area),1,'所有地区',area) 地区,SUM(personincome)
FROM earnings
GROUP BY cube(earnmonth,area) ORDER by earnmonth,area NULLS last;

结果为:

image.png

8. max(),min(),avg()和sum()函数综合运用

安装月份和地区统计打工收入最高值,最低值,平均值和总额.
SELECT DISTINCT earnmonth 月份,area 地区,
       MAX(personincome) over(PARTITION BY earnmonth,area) 最高值,
       min(personincome) OVER(PARTITION BY earnmonth,area) 最低值,
       AVG(personincome) over(PARTITION BY earnmonth,area) 平均值,
       sum(personincome) over(PARTITION BY earnmonth,area) 总额
       FROM earnings;

以上语句统计结果和如下语句使用group by的查询结果一样:

SELECT earnmonth 月份,area 地区,
       MAX(personincome) 最高值,
       min(personincome) 最低值,
       AVG(personincome) 平均值,
       sum(personincome) 总额
       FROM earnings GROUP BY earnmonth,area;

9. lag( )和lead( )函数

说明:Lag和Lead函数可以在一次查询中取出某个字段的前N行和后N行的数据(可以是其他字段的数据,比如根据字段甲查询上一行或下两行的字段乙),原来没有分析函数的时候采用子查询方法,但是比较麻烦:

语法如下:

lag(value_expression [,offset] [,default]) over ([query_partition_clase] order_by_clause);

lead(value_expression [,offset] [,default]) over ([query_partition_clase] order_by_clause);

其中:

value_expression:可以是一个字段或一个内建函数。

offset是正整数,默认为1,指往前或往后几点记录.因组内第一个条记录没有之前的行,最后一行没有之后的行,

default就是用于处理这样的信息,默认为空。

统计每个打工者上个月和下个月有没有赚钱(personincome大于0即为赚钱):

select earnmonth 本月,sname 打工者, 
       lag(decode(nvl(personincome,0),0,'没赚','赚了'),1,0) over(partition by sname order by earnmonth) 上月, 
       lead(decode(nvl(personincome,0),0,'没赚','赚了'),1,0) over(partition by sname order by earnmonth) 下月 
from earnings;

结果为:

image.png



实验数据:

1.建表

create table earnings -- 打工赚钱表 
( 
  earnmonth varchar2(6), -- 打工月份 
  area varchar2(20), -- 打工地区 
  sno varchar2(10), -- 打工者编号 
  sname varchar2(20), -- 打工者姓名 
  times int, -- 本月打工次数 
  singleincome number(10,2), -- 每次赚多少钱 
  personincome number(10,2) -- 当月总收入 
) ;

2.插入实验数据

insert into earnings values('200912','北平','511601','大魁',11,30,11*30); 
insert into earnings values('200912','北平','511602','大凯',8,25,8*25); 
insert into earnings values('200912','北平','511603','小东',30,6.25,30*6.25); 
insert into earnings values('200912','北平','511604','大亮',16,8.25,16*8.25); 
insert into earnings values('200912','北平','511605','贱敬',30,11,30*11); 
insert into earnings values('200912','金陵','511301','小玉',15,12.25,15*12.25); 
insert into earnings values('200912','金陵','511302','小凡',27,16.67,27*16.67); 
insert into earnings values('200912','金陵','511303','小妮',7,33.33,7*33.33); 
insert into earnings values('200912','金陵','511304','小俐',0,18,0); 
insert into earnings values('200912','金陵','511305','雪儿',11,9.88,11*9.88); 
insert into earnings values('201001','北平','511601','大魁',0,30,0); 
insert into earnings values('201001','北平','511602','大凯',14,25,14*25); 
insert into earnings values('201001','北平','511603','小东',19,6.25,19*6.25); 
insert into earnings values('201001','北平','511604','大亮',7,8.25,7*8.25); 
insert into earnings values('201001','北平','511605','贱敬',21,11,21*11); 
insert into earnings values('201001','金陵','511301','小玉',6,12.25,6*12.25); 
insert into earnings values('201001','金陵','511302','小凡',17,16.67,17*16.67); 
insert into earnings values('201001','金陵','511303','小妮',27,33.33,27*33.33); 
insert into earnings values('201001','金陵','511304','小俐',16,18,16*18); 
insert into earnings values('201001','金陵','511305','雪儿',11,9.88,11*9.88); 
commit;

3.查询展示全表

SELECT * FROM earnings;
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容