Oracle分析函数

懵懂的女人 提交于 2019-11-30 03:25:16

分析函数

OLAP的系统(即Online Aanalyse Process)一般用于系统决策使用。通常和数据仓库、数据分析、数据挖掘等概念联系在一起。这些系统的特点是数据量大,对实时响应的要求不高或者根本不关注这方面的要求,以查询、统计操作为主。

我们来看看下面的几个典型例子:
①查找上一年度各个销售区域排名前10的员工
②按区域查找上一年度订单总额占区域订单总额20%以上的客户
③查找上一年度销售最差的部门所在的区域
④查找上一年度销售最好和最差的产品

我们看看上面的几个例子就可以感觉到这几个查询和我们日常遇到的查询有些不同,具体有:

①需要对同样的数据进行不同级别的聚合操作
②需要在表内将多条数据和同一条数据进行多次的比较
③需要在排序完的结果集上进行额外的过滤操作

分析函数和聚合函数的不同之处是什么?
普通的聚合函数用group by分组,每个分组返回一个统计值,而分析函数采用partition by分组,并且每组每行都可以返回一个统计值。

分析函数的形式
分析函数带有一个开窗函数over(),包含三个分析子句:分组(partition by), 排序(order by), 窗口(rows) ,他们的使用形式如下:

function_name(<argument>,<argument>...) over(<partition_Clause><order by_Clause><windowing_Clause>);
  • function_name():函数名称
  • argument:参数
  • over( ):开窗函数
  • partition_Clause:分区子句,数据记录集分组,group by...
  • order by_Clause:排序子句,数据记录集排序,order by...
  • windowing_Clause:开窗子句,定义分析函数在操作行的集合,三种开窗方式:rows、range、Specifying

注:使用开窗子句时一定要有排序子句!!!

-- 按区域查找上一年度订单总额占区域订单总额20%以上的客户 table : orders_tmp
-- 1.找出2001年度区域订单总额

select o.cust_nbr customer,o.region_id region,sum(o.tot_sales) cust_sales,
sum(sum(o.tot_sales)) over(partition by o.region_id) region_sales
 from orders_tmp o where o.year = 2001 group by o.region_id, o.cust_nbr;

-- 2.在1的基础上得出订单总额占区域订单总额20%以上的客户

复制代码

select * from 
(select o.cust_nbr customer, o.region_id region,
     sum(o.tot_sales) cust_sales,
     sum(sum(o.tot_sales)) over(partition by o.region_id) region_sales
     from orders_tmp o where o.year = 2001
     group by o.region_id, o.cust_nbr) all_sales
 where all_sales.cust_sales > all_sales.region_sales * 0.2;

复制代码

-- 加上百分比 round()

复制代码

select cust_nbr, region_id, cust_sales, region_sales,  -- 此处可以用tmptb.* , 但不能用 *
100 * round(cust_sales / region_sales, 2) || '%' Percent from 
 (select cust_nbr, region_id,
 sum(TOT_SALES) cust_sales,
 sum(sum(tot_sales)) over(partition by REGION_ID) as region_sales
 from orders_tmp where o.year = 2001 group by CUST_NBR, REGION_ID 
 order by REGION_ID) tmptb
 where cust_sales > region_sales * 0.2;
 (Rank, Dense_rank, row_number)

复制代码

①ROW_NUMBER:12345

Row_number函数返回一个唯一的值,当碰到相同数据时,排名按照记录集中记录的顺序依次递增。

②DENSE_RANK:12223

Dense_rank函数返回一个唯一的值,除非当碰到相同数据时,此时所有相同数据的排名都是一样的。

③RANK:12225

Rank函数返回一个唯一的值,除非遇到相同的数据时,此时所有相同数据的排名是一样的,

同时会在最后一条相同记录和下一条不同记录的排名之间空出排名。

-- ①对所有客户按订单总额进行排名
-- ②按区域和客户订单总额进行排名
-- ③找出订单总额排名前13位的客户
-- ④找出订单总额最高、最低的客户
-- ⑤找出订单总额排名前25%的客户

-- 筛选排名前12位的客户, table : user_order
-- 1.对所有客户按订单总额进行排名, 使用rownum , rownum = 13,14 的数据跟 12 的数据一样, 但是被漏掉了

select rownum, tmptb.* from 
 (select * from user_order order by CUSTOMER_sales desc) tmptb
 where rownum <= 12;

-- 2.按区域和客户订单总额进行排名 Rank, Dense_rank, row_number

复制代码

select region_id, customer_id, 
sum(customer_sales) total,
rank() over(partition by region_id order by sum(customer_sales) desc) rank,
dense_rank() over(partition by region_id order by sum(customer_sales) desc) dense_rank,
 row_number() over(partition by region_id order by sum(customer_sales) desc) row_number
 from user_order
 group by region_id, customer_id;

复制代码

Top/Bottom N
First/Last
NTile

-- ①对所有客户按订单总额进行排名
-- ②按区域和客户订单总额进行排名
-- ③找出订单总额排名前13位的客户
-- ④找出订单总额最高、最低的客户
-- ⑤找出订单总额排名前25%的客户

-- 此处 null 被排到第一位 , 可以加 nulls last 把null的数据放到最后

select region_id, customer_id,
sum(customer_sales) cust_sales,
sum(sum(customer_sales)) over(partition by region_id) ran_total,
 rank() over(partition by region_id order by sum(customer_sales) desc /* nulls last */) rank
 from user_order
 group by region_id, customer_id;

-- 找出所有订单总额排名前3的大客户

复制代码

select * from 
(select region_id,
    customer_id,
    sum(customer_sales) cust_total,
     rank() over(order by sum(customer_sales) desc NULLS LAST) rank
     from user_order
     group by region_id, customer_id)
 where rank <= 3;

复制代码

-- 找出每个区域订单总额排名前3的大客户

复制代码

select *
from (select region_id,
    customer_id,
    sum(customer_sales) cust_total,
     sum(sum(customer_sales)) over(partition by region_id) reg_total,
     rank() over(partition by region_id order by sum(customer_sales) desc NULLS LAST) rank
     from user_order
     group by region_id, customer_id)
 where rank <= 3;

复制代码

-- min keep first last 找出订单总额最高、最低的客户
-- Min只能用于 dense_rank
-- min 函数的作用是用于当存在多个First/Last情况下保证返回唯一的记录, 去掉会出错
-- keep的作用。告诉Oracle只保留符合keep条件的记录。

select 
min(customer_id) keep (dense_rank first order by sum(customer_sales) desc) first,
min(customer_id) keep (dense_rank last order by sum(customer_sales) desc) last
 from user_order
 group by customer_id;

-- 出订单总额排名前1/5的客户 ntile
-- 1.将数据分成5块

select region_id,customer_id,
 sum(customer_sales) sales,
 ntile(5) over(order by sum(customer_sales) desc nulls last) tile
 from user_order
 group by region_id, customer_id;

-- 2.提取 tile=1 的数据

复制代码

select * from 
(select region_id,customer_id,
sum(customer_sales) sales,
ntile(5) over(order by sum(customer_sales) desc nulls last) tile
from user_order
 group by region_id, customer_id)
 where tile = 1;

复制代码

-- cust_nbr,month 为主键, 去重,只留下month最大的记录
-- 查找 cust_nbr 相同, month 最大的记录

select cust_nbr,
 max(month) keep(dense_rank first order by month desc) max_month
 from orders_tmp group by cust_nbr;

-- 去重, cust_nbr,month 为主键, cust_nbr 相同,只留下month最大的记录

delete from orders_tmp2 where (cust_nbr, month) not in 
 (select cust_nbr,
 max(month) keep(dense_rank first order by month desc) max_month
 from orders_tmp2 tb group by cust_nbr)

窗口函数

first_value/last_value

rows between ...preceding and ... following

range between interval

current row

lag(sum(tot_sales),1), lead

-- ①列出每月的订单总额以及全年的订单总额
-- ②列出每月的订单总额以及截至到当前月的订单总额
-- ③列出上个月、当月、下一月的订单总额以及全年的订单总额
-- ④列出每天的营业额及一周来的总营业额
-- ⑤列出每天的营业额及一周来每天的平均营业额

-- ①通过指定一批记录:例如从当前记录开始直至某个部分的最后一条记录结束
-- ②通过指定一个时间间隔:例如在交易日之前的前30天
-- ③通过指定一个范围值:例如所有占到当前交易量总额5%的记录

-- 列出每月的订单总额以及全年的订单总额
1.实现方法1

select month,
sum(tot_sales) month_sales,
 sum(sum(tot_sales)) over (order by month rows between unbounded preceding and unbounded following) total_sales
 from orders
 group by month;

2.实现方法2

select month,
sum(tot_sales) month_sales,
 sum(sum(tot_sales)) over(/*order by month*/) all_sales  -- 加上Order by month , 则数逐条记录递增
from orders group by month;

-- 列出每月的订单总额以及截至到当前月的订单总额
1.实现方法1

select month,
sum(tot_sales) month_sales,
 sum(sum(tot_sales)) over(order by month rows between unbounded preceding and current row) current_total_sales
 from orders group by month;

2.实现方法2

select month,
sum(tot_sales) month_sales,
sum(sum(tot_sales)) over(order by month) all_sales  -- 加上Order by month , 则是前面记录累加到当前记录
from orders group by month;

-- 有时可能是针对全年的数据求平均值,有时会是针对截至到当前的所有数据求平均值。很简单,只需要将:
-- sum(sum(tot_sales))换成avg(sum(tot_sales))即可。

-- 统计当天销售额和五天内的平均销售额 range between interval

复制代码

select trunc(order_dt) day,
sum(sale_price) daily_sales,
avg(sum(sale_price)) over 
(order by trunc(order_dt) range between interval '2' day preceding and interval '2' day following) five_day_avg
 from cust_order
 where sale_price is not null 
 and order_dt between to_date('01-jul-2001','dd-mon-yyyy')
 and to_date('31-jul-2001','dd-mon-yyyy')

复制代码

-- 显示当前月、上一个月、后一个月的销售情况,以及每3个月的销售平均值

复制代码

select month,
first_value(sum(tot_sales)) over 
(order by month rows between 1 preceding and 1 following) prev_month,
sum(tot_sales) monthly_sales,
 last_value(sum(tot_sales)) over 
 (order by month rows between 1 preceding and 1 following) next_month,
 avg(sum(tot_sales)) over 
 (order by month rows between 1 preceding and 1 following) rolling_avg
 from orders_tmp
 where year = 2001 and region_id = 6
 group by month order by month;

复制代码

-- 显示当月的销售额和上个月的销售额
-- first_value(sum(tot_sales) over (order by month rows between 1 precedingand 0 following))
-- lag(sum(tot_sales),1)中的1表示以1月为间隔基准, 对应为lead

select  month,            
sum(tot_sales) monthly_sales,
 lag(sum(tot_sales), 1) over (order by month) prev_month_sales
 from orders_tmp
 where year = 2001 and region_id = 6
 group by month order by month;

1、first_value()与last_value():求最值对应的其他属性。

2、rank(),dense_rank()与row_number(),ntile(n):求排序

rank,dense_rank,row_number函数为每条记录产生一个从1开始至n的自然数,n的值可能小于等于记录的总数。这3个函数的唯一区别在于当碰到相同数据时的排名策略。
①row_number: 返回一个唯一的值,当碰到相同数据时,排名按照记录集中记录的顺序依次递增。
②dense_rank: 返回一个唯一的值,当碰到相同数据时,此时所有相同数据的排名都是一样的。first、last :从DENSE_RANK返回的集合中取出排在最后面的一个值的行
③rank: 返回一个唯一的值,当碰到相同的数据时,此时所有相同数据的排名是一样的,同时会在最后一条相同记录和下一条不同记录的排名之间空出排名。

3、lag()与lead():求之前或之后的第N行

lag和lead函数可以在一次查询中取出同一字段的前n行的数据和后n行的值。这种操作可以使用对相同表的表连接来实现,不过使用lag和lead有更高的效率。
lag(arg1,arg2,arg3)
第一个参数是列名,
第二个参数是偏移的offset,
第三个参数是超出记录窗口时的默认值。

4、rollup()、cube()和grouping():排列组合分组

1)、group by rollup(a, b, c):
首先会对(a、b、c)进行group by,
然后再对(a、b)进行group by,
其后再对(a)进行group by,
最后对全表进行汇总操作。

2)、group by cube(a, b, c):
则首先会对(a、b、c)进行group by,
然后依次是(a、b),(a、c),(a),(b、c),(b),(c),
最后对全表进行汇总操作。

5、count()、max(),min(),sum()与avg():求移动的最值总和与平均值

6、ratio_to_report(a) over(partition by b) :求按照b分组后a的值在所属分组中总值的占比,a的值必须为数值或数值型字段



005_结果

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!