知识大全 Oracle分析函数详述

Posted 函数

篇首语:鸟欲高飞先振翅,人求上进先读书。本文由小常识网(cha138.com)小编为大家整理,主要介绍了知识大全 Oracle分析函数详述相关的知识,希望对你有一定的参考价值。

Oracle分析函数详述  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!

  一 分析函数(OVER)

  目录

  ===============================================

   Oracle分析函数简介

   Oracle分析函数简单实例

   分析函数OVER解析

  一 Oracle分析函数简介

  在日常的生产环境中 我们接触得比较多的是OLTP系统(即Online Transaction Process) 这些系统的特点是具备实时要求 或者至少说对响应的时间多长有一定的要求 其次这些系统的业务逻辑一般比较复杂 可能需要经过多次的运算 比如我们经常接触到的电子商城

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

  我们来看看下面的几个典型例子

  ①查找上一年度各个销售区域排名前 的员工

  ②按区域查找上一年度订单总额占区域订单总额 %以上的客户

  ③查找上一年度销售最差的部门所在的区域

  ④查找上一年度销售最好和最差的产品

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

  ①需要对同样的数据进行不同级别的聚合操作

  ②需要在表内将多条数据和同一条数据进行多次的比较

  ③需要在排序完的结果集上进行额外的过滤操作

  二 Oracle分析函数简单实例

  下面我们通过一个实际的例子 按区域查找上一年度订单总额占区域订单总额 %以上的客户 来看看分析函数的应用

  【 】测试环境

  SQL> desc orders_tmp;

  Name                           Null?    Type

  

  CUST_NBR                    NOT NULL NUMBER( )

  REGION_ID                   NOT NULL NUMBER( )

  SALESPERSON_ID      NOT NULL NUMBER( )

  YEAR                              NOT NULL NUMBER( )

  MONTH                         NOT NULL NUMBER( )

  TOT_ORDERS              NOT NULL NUMBER( )

  TOT_SALES                 NOT NULL NUMBER( )

  【 】测试数据

  SQL> select * from orders_tmp;

  CUST_NBR  REGION_ID SALESPERSON_ID       YEAR      MONTH TOT_ORDERS  TOT_SALES

  

                                                                          

                                                                           

                                                                              

                                                                            

                                                                            

                                                                             

                                                                            

                                                                             

                                                                             

                                                                             

                                                                              

                                                                            

                                                                            

   rows selected

  【 】测试语句

  SQL> 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 =

      group by o region_id o cust_nbr;

  CUSTOMER     REGION CUST_SALES REGION_SALES

  

                              

                                

                             

                             

  三 分析函数OVER解析

  请注意上面的绿色高亮部分 group by的意图很明显 将数据按区域ID 客户进行分组 那么Over这一部分有什么用呢?假如我们只需要统计每个区域每个客户的订单总额 那么我们只需要 group by o region_id o cust_nbr就够了 但我们还想在每一行显示该客户所在区域的订单总额 这一点和前面的不同 需要在前面分组的基础上按区域累加 很显然group by和sum是无法做到这一点的(因为聚集操作的级别不一样 前者是对一个客户 后者是对一批客户)

  这就是over函数的作用了!它的作用是告诉SQL引擎 按区域对数据进行分区 然后累积每个区域每个客户的订单总额(sum(sum(o tot_sales)))

  现在我们已经知道 年度每个客户及其对应区域的订单总额 那么下面就是筛选那些个人订单总额占到区域订单总额 %以上的大客户了

  SQL> 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 =

              group by o region_id o cust_nbr) all_sales

      where all_sales cust_sales > all_sales region_sales * ;

  CUSTOMER     REGION CUST_SALES REGION_SALES

  

                          

                          

                          

  SQL>

  现在我们已经知道这些大客户是谁了!哦 不过这还不够 如果我们想要知道每个大客户所占的订单比例呢?看看下面的SQL语句 只需要一个简单的Round函数就搞定了 SQL> select all_sales *

            * round(cust_sales / region_sales ) || % Percent

       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 =

              group by o region_id o cust_nbr) all_sales

      where all_sales cust_sales > all_sales region_sales * ;

  CUSTOMER     REGION CUST_SALES REGION_SALES PERCENT

  

                                             %

                                              %

                                            %

  SQL>

  总结

  ①Over函数指明在那些字段上做分析 其内跟Partition by表示对数据进行分组 注意Partition by可以有多个字段

  ②Over函数可以和其它聚集函数 分析函数搭配 起到不同的作用 例如这里的SUM 还有诸如Rank Dense_rank等

  一 分析函数 (rank\\dense_rank\\row_number)

  目录

  ===============================================

   使用rownum为记录排名

   使用分析函数来为记录排名

   使用分析函数为记录进行分组排名

  一 使用rownum为记录排名

  在前面一篇《Oracle开发专题之 分析函数》 我们认识了分析函数的基本应用 现在我们再来考虑下面几个问题

  ①对所有客户按订单总额进行排名

  ②按区域和客户订单总额进行排名

  ③找出订单总额排名前 位的客户

  ④找出订单总额最高 最低的客户

  ⑤找出订单总额排名前 %的客户

  按照前面第一篇文章的思路 我们只能做到对各个分组的数据进行统计 如果需要排名的话那么只需要简单地加上rownum不就行了吗?事实情况是否如此想象般简单 我们来实践一下

  【 】测试环境

  SQL> desc user_order;

  Name                                      Null?    Type

  

  REGION_ID                                          NUMBER( )

  CUSTOMER_ID                                  NUMBER( )

  CUSTOMER_SALES                          NUMBER

  【 】测试数据

  SQL> select * from user_order order by customer_sales;

  REGION_ID CUSTOMER_ID CUSTOMER_SALES

  

                           

                         

                           

                        

                       

                       

                       

                         

                         

                         

                       

                       

                       

                       

                         

                      

                               

                             

                             

                       

                       

                       

                      

                       

                         

                      

                         

                       

                       

                       

   rows selected

  注意这里有 条记录的订单总额是一样的 假如我们现在需要筛选排名前 位的客户 如果使用rownum会有什么样的后果呢? SQL> select rownum t *

       from (select *

               from user_order

              order by customer_sales desc) t

      where rownum <=

      order by customer_sales desc;

  ROWNUM  REGION_ID CUSTOMER_ID CUSTOMER_SALES

  

                                     

                                     

                                     

                                       

                                   

                                       

                                     

                                   

                                    

                                   

                                   

                                              

   rows selected

  很明显假如只是简单地按rownum进行排序的话 我们漏掉了另外两条记录(参考上面的结果)

  二 使用分析函数来为记录排名

  针对上面的情况 Oracle从 i开始就提供了 个分析函数 rand dense_rank row_number来解决诸如此类的问题 下面我们来看看这 个分析函数的作用以及彼此之间的区别

  Rank Dense_rank Row_number函数为每条记录产生一个从 开始至N的自然数 N的值可能小于等于记录的总数 这 个函数的唯一区别在于当碰到相同数据时的排名策略

  ①ROW_NUMBER

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

  ②DENSE_RANK

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

  ③RANK

  Rank函数返回一个唯一的值 除非遇到相同的数据时 此时所有相同数据的排名是一样的 同时会在最后一条相同记录和下一条不同记录的排名之间空出排名

  这样的介绍有点难懂 我们还是通过实例来说明吧 下面的例子演示了 个不同函数在遇到相同数据时不同排名策略

  SQL> select region_id customer_id sum(customer_sales) total

            rank() over(order by sum(customer_sales) desc) rank

            dense_rank() over(order by sum(customer_sales) desc) dense_rank

            row_number() over(order by sum(customer_sales) desc) row_number

       from user_order

      group by region_id customer_id;

  REGION_ID CUSTOMER_ID      TOTAL       RANK DENSE_RANK ROW_NUMBER

  

                                                       

                                                         

                                                       

                                                       

                                                           

   rows selected

  请注意上面的绿色高亮部分 这里生动的演示了 种不同的排名策略

  ①对于第一条相同的记录 种函数的排名都是一样的

  ②当出现第二条相同的记录时 Rank和Dense_rank依然给出同样的排名 而row_number则顺延递增为 依次类推至第三条相同的记录

  ③当排名进行到下一条不同的记录时 可以看到Rank函数在 和 之间空出了 的排名 因为这 个排名实际上已经被第二 三条相同的记录占了 而Dense_rank则顺序递增 row_number函数也是顺序递增

  比较上面 种不同的策略 我们在选择的时候就要根据客户的需求来定夺了

  ①假如客户就只需要指定数目的记录 那么采用row_number是最简单的 但有漏掉的记录的危险

  ②假如客户需要所有达到排名水平的记录 那么采用rank或dense_rank是不错的选择 至于选择哪一种则看客户的需要 选择dense_rank或得到最大的记录

  三 使用分析函数为记录进行分组排名

  上面的排名是按订单总额来进行排列的 现在跟进一步 假如是为各个地区的订单总额进行排名呢?这意味着又多了一次分组操作 对记录按地区分组然后进行排名 幸亏Oracle也提供了这样的支持 我们所要做的仅仅是在over函数中order by的前面增加一个分组子句 partition by region_id

  SQL> 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;

  REGION_ID CUSTOMER_ID      TOTAL       RANK DENSE_RANK ROW_NUMBER

  

                                                           

                                                           

                                                           

                                                           

                                                           

                                                         

   rows selected

  现在我们看到的排名将是基于各个地区的 而非所有区域的了!Partition by 子句在排列函数中的作用是将一个结果集划分成几个部分 这样排列函数就能够应用于这各个子集

  三 分析函数 (top\\bottom n first\\last ntile)

  目录

  ===============================================

   带空值的排列

   Top/Bottom N查询

   First/Last排名查询

   按层次查询

  一 带空值的排列

  在前面《Oracle开发专题之 分析函数 (Rank Dense_rank row_number)》一文中 我们已经知道了如何为一批记录进行全排列 分组排列 假如被排列的数据中含有空值呢?

  SQL> 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) rank

       from user_order

      group by region_id customer_id;

  REGION_ID CUSTOMER_ID CUST_SALES  RAN_TOTAL       RANK

  

                                          

                              

                              

                              

                               

                               

  我们看到这里有一条记录的CUST_TOTAL字段值为NULL 但居然排在第一名了!显然这不符合情理 所以我们重新调整完善一下我们的排名策略 看看下面的语句

  SQL> 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;

  REGION_ID CUSTOMER_ID CUST_TOTAL  REG_TOTAL       RANK

  

                                

                               

                               

                                 

                                 

                                            

  绿色高亮处 NULLS LAST/FIRST告诉Oracle让空值排名最后后第一

  注意是NULLS 不是NULL

  二 Top/Bottom N查询

  在日常的工作生产中 我们经常碰到这样的查询 找出排名前 位的订单客户 找出排名前 位的销售人员等等 现在这个对我们来说已经是很简单的问题了 下面我们用一个实际的例子来演示

  【 】找出所有订单总额排名前 的大客户

  SQL> select *

  SQL>   from (select region_id

  SQL>                customer_id

  SQL>                sum(customer_sales) cust_total

  SQL>                rank() over(order by sum(customer_sales) desc NULLS LAST) rank

  SQL>           from user_order

  SQL>          group by region_id customer_id)

  SQL>  where rank <= ;

  REGION_ID CUSTOMER_ID CUST_TOTAL       RANK

  

                          

                          

                          

  SQL>

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

  SQL> 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 <= ;

  REGION_ID CUSTOMER_ID CUST_TOTAL  REG_TOTAL       RANK

  

                               

                               

                               

                               

                               

                              

                              

                              

                              

                              

                              

                              

                              

                              

                              

                              

                              

                              

   rows selected

  三 First/Last排名查询

  想象一下下面的情形 找出订单总额最多 最少的客户 按照前面我们学到的知识 这个至少需要 个查询 第一个查询按照订单总额降序排列以期拿到第一名 第二个查询按照订单总额升序排列以期拿到最后一名 是不是很烦?因为Rank函数只告诉我们排名的结果 却无法自动替我们从中筛选结果

  幸好Oracle为我们在排列函数之外提供了两个额外的函数 first last函数 专门用来解决这种问题 还是用实例说话 SQL> 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;

  FIRST       LAST

  

            

  这里有几个看起来比较疑惑的地方

  ①为什么这里要用min函数

  ②Keep这个东西是干什么的

  ③fist/last是干什么的

  ④dense_rank和dense_rank()有什么不同 能换成rank吗?

  首先解答一下第一个问题 min函数的作用是用于当存在多个First/Last情况下保证返回唯一的记录 假如我们去掉会有什么样的后果呢? SQL> select keep (dense_rank first order by sum(customer_sales) desc) first

                keep (dense_rank last order by sum(customer_sales) desc) last

       from user_order

      group by customer_id;

  select keep (dense_rank first order by sum(customer_sales) desc) first

  *

  ERROR at line :

  ORA : missing right parenthesis

  接下来看看第 个问题 keep是干什么用的?从上面的结果我们已经知道Oracle对排名的结果只 保留 条数据 这就是keep的作用 告诉Oracle只保留符合keep条件的记录

  那么什么才是符合条件的记录呢?这就是第 个问题了 dense_rank是告诉Oracle排列的策略 first/last则告诉最终筛选的条件

  第 个问题 如果我们把dense_rank换成rank呢? SQL> select min(region_id)

             keep(rank first order by sum(customer_sales) desc) first

            min(region_id)

             keep(rank last order by sum(customer_sales) desc) last

       from user_order

      group by region_id;

  select min(region_id)

  *

  ERROR at line :

  ORA : missing DENSE_RANK

  四 按层次查询

  现在我们已经见识了如何通过Oracle的分析函数来获取Top/Bottom N 第一个 最后一个记录 有时我们会收到类似下面这样的需求 找出订单总额排名前 / 的客户

  很熟悉是不?我们马上会想到第二点中提到的方法 可是rank函数只为我们做好了排名 并不知道每个排名在总排名中的相对位置 这时候就引入了另外一个分析函数NTile 下面我们就以上面的需求为例来讲解一下

  SQL> select region_id

            customer_id

            ntile( ) over(order by sum(customer_sales) desc) til

       from user_order

      group by region_id customer_id;

  REGION_ID  CUSTOMER_ID       TILE

  

                      

                       

                      

                         

                       

                         

                       

                         

                       

                         

                         

                       

                         

                      

                         

cha138/Article/program/Oracle/201311/18316

相关参考

知识大全 Oracle 9i 分析函数参考手册

Oracle9i分析函数参考手册  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  Oracle从开

知识大全 Oracle使用hash分区优化分析函数查询

Oracle使用hash分区优化分析函数查询  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  在O

知识大全 新手上路:Oracle分析函数学习笔记一

新手上路:Oracle分析函数学习笔记一  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  环境wi

知识大全 oracle的分析函数over(Partition by...)

oracle的分析函数over(Partitionby...)  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来

知识大全 利用Oracle分析函数实现多行数据合并为一行

利用Oracle分析函数实现多行数据合并为一行  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  d

知识大全 Oracle 数据对象分析

Oracle数据对象分析  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  过程和函数    过程和

知识大全 Oracle通用函数,分组函数,子查询

Oracle通用函数,分组函数,子查询  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  注意)Or

知识大全 Oracle中sign函数和decode函数的使用

Oracle中sign函数和decode函数的使用  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧! 

知识大全 Oracle聚集函数排序

Oracle聚集函数排序  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  对于按照自定义聚集函数连

知识大全 Oracle随机函数调用

Oracle认证:Oracle随机函数调用  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!Oracl