Oracle开发之分析函数(Top/Bottom N、First/Last、NTile)_oracle

一、带空值的排列:

在前面《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
---------- ----------- ---------- ---------- ----------
        10          31                    6238901          1
        10          26    1808949    6238901          2
        10          27    1322747    6238901          3
        10          30    1216858    6238901          4
        10          28     986964    6238901          5
        10          29     903383    6238901          6

我们看到这里有一条记录的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
---------- ----------- ---------- ---------- ----------
        10          26    1808949     6238901           1
        10          27    1322747    6238901           2
        10          30    1216858    6238901           3
        10          28     986964     6238901           4
        10          29     903383     6238901           5
        10          31     6238901                           6

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

注意是NULLS,不是NULL。

二、Top/Bottom N查询:

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

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

复制代码 代码如下:

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

 REGION_ID CUSTOMER_ID CUST_TOTAL       RANK
---------- ----------- ---------- ----------
         9          25    2232703          1
         8          17    1944281          2
         7          14    1929774          3

SQL>

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

复制代码 代码如下:

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

 REGION_ID CUSTOMER_ID CUST_TOTAL  REG_TOTAL       RANK
---------- ----------- ---------- ---------- ----------
         5           4    1878275    5585641          1
         5           2    1224992    5585641          2
         5           5    1169926    5585641          3
         6           6    1788836    6307766          1
         6           9    1208959    6307766          2
         6          10    1196748    6307766          3
         7          14    1929774    6868495          1
         7          13    1310434    6868495          2
         7          15    1255591    6868495          3
         8          17    1944281    6854731          1
         8          20    1413722    6854731          2
         8          18    1253840    6854731          3
         9          25    2232703    6739374          1
         9          23    1224992    6739374          2
         9          24    1224992    6739374          2
        10          26    1808949    6238901          1
        10          27    1322747    6238901          2
        10          30    1216858    6238901          3

18 rows selected.

三、First/Last排名查询:

想象一下下面的情形:找出订单总额最多、最少的客户。按照前面我们学到的知识,这个至少需要2个查询。第一个查询按照订单总额降序排列以期拿到第一名,第二个查询按照订单总额升序排列以期拿到最后一名。是不是很烦?因为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
---------- ----------
        31          1

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

①为什么这里要用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 1:
ORA-00907: missing right parenthesis

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

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

第4个问题:如果我们把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 1:
ORA-02000: missing DENSE_RANK

四、按层次查询:

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

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

复制代码 代码如下:

SQL> select region_id,
         customer_id,
         ntile(5) over(order by sum(customer_sales) desc) til
    from user_order
   group by region_id, customer_id;

 REGION_ID CUSTOMER_ID       TILE
---------- ----------- ----------
        10          31          1
         9          25           1
        10          26          1
         6           6            1        
         8          18           2
         5           2            2
         9          23           3
         6           9            3
         7          11           3
         5           3            4
         6           8            4
         8          16           4
         6           7            5
        10          29          5
         5           1            5

Ntil函数为各个记录在记录集中的排名计算比例,我们看到所有的记录被分成5个等级,那么假如我们只需要前1/5的记录则只需要截取TILE的值为1的记录就可以了。假如我们需要排名前25%的记录(也就是1/4)那么我们只需要设置ntile(4)就可以了。

时间: 2024-10-13 15:17:35

Oracle开发之分析函数(Top/Bottom N、First/Last、NTile)_oracle的相关文章

Oracle开发之分析函数总结_oracle

这一篇是对前面所有关于分析函数的文章的总结: 一.统计方面: 复制代码 代码如下: Sum() Over ([Partition by ] [Order by ])    Sum() Over ([Partition by ] [Order by ]       Rows Between  Preceding And  Following)        Sum() Over ([Partition by ] [Order by ]      Rows Between  Preceding An

Oracle开发之分析函数(Rank, Dense_rank, row_number)_oracle

一.使用rownum为记录排名: 在前面一篇<Oracle开发之分析函数简介Over>,我们认识了分析函数的基本应用,现在我们再来考虑下面几个问题: ①对所有客户按订单总额进行排名 ②按区域和客户订单总额进行排名 ③找出订单总额排名前13位的客户 ④找出订单总额最高.最低的客户 ⑤找出订单总额排名前25%的客户 按照前面第一篇文章的思路,我们只能做到对各个分组的数据进行统计,如果需要排名的话那么只需要简单地加上rownum不就行了吗?事实情况是否如此想象般简单,我们来实践一下. [1]测试环境

Oracle开发之分析函数简介Over用法_oracle

一.Oracle分析函数简介: 在日常的生产环境中,我们接触得比较多的是OLTP系统(即Online Transaction Process),这些系统的特点是具备实时要求,或者至少说对响应的时间多长有一定的要求:其次这些系统的业务逻辑一般比较复杂,可能需要经过多次的运算.比如我们经常接触到的电子商城. 在这些系统之外,还有一种称之为OLAP的系统(即Online Aanalyse Process),这些系统一般用于系统决策使用.通常和数据仓库.数据分析.数据挖掘等概念联系在一起.这些系统的特点

Oracle开发之窗口函数_oracle

一.窗口函数简介: 到目前为止,我们所学习的分析函数在计算/统计一段时间内的数据时特别有用,但是假如计算/统计需要随着遍历记录集的每一条记录而进行呢?举些例子来说: ①列出每月的订单总额以及全年的订单总额 ②列出每月的订单总额以及截至到当前月的订单总额 ③列出上个月.当月.下一月的订单总额以及全年的订单总额 ④列出每天的营业额及一周来的总营业额 ⑤列出每天的营业额及一周来每天的平均营业额 仔细回顾一下前面我们介绍到的分析函数,我们会发现这些需求和前面有一些不同:前面我们介绍的分析函数用于计算/统

Oracle开发人员 JAVA存储过程

oracle|存储过程  Oracle开发人员 JAVA存储过程 利用Java存储过程简化数据库操作 作者:Kuassi Mensah 利用Java存储过程沟通SQL.XML.Java.J2EE和Web服务. 存储过程(stored procedure)允许将运行于数据库层中的持久性逻辑与运行于中间层中的商务逻辑有效地分离开来.这种分离可以降低整个应用程序的复杂性,并提供其重用性.安全性.性能和可伸缩性. 但是,妨碍存储过程广泛采用的一个主要障碍是不同数据库厂商使用各种专有的.且依赖于数据库的实

ORACLE中实现SELECT TOP N的方法

1.在ORACLE中实现SELECT TOP N 由于ORACLE不支持SELECT TOP语句,所以在ORACLE中经常是用ORDER BY跟ROWNUM的组合来实现SELECT TOP N的查询. 简单地说,实现方法如下所示: SELECT 列名1...列名n FROM (SELECT 列名1...列名n FROM 表名 ORDER BY 列名1...列名n) WHERE ROWNUM <= N(抽出记录数) ORDER BY ROWNUM ASC 下面举个例子简单说明一下. 顾客表cust

LINQ to SQL语句(9)之Top/Bottom和Paging和SqlMethods

Top/Bottom操作 适用场景:适量的取出自己想要的数据,不是全部取出, 这样性能有所加强. Take 说明:获取集合的前n个元素:延迟.即只 返回限定数量的结果集. var q = ( from e in db.Employees orderby e.HireDate select e) .Take(5); 语句描述:选择所雇用的前5个雇员. Skip 说 明:跳过集合的前n个元素:延迟.即我们跳过给定的数目返回后面的结果集. var q = ( from p in db.Products

八种主要的Oracle开发工具简介

Session领导人兼Apps Associates创始人.CEO Sridhar Bogelli是基于Mass应用程序开发顾问,告诫 Oracle开发人员要恰当运用Oracle和第三方销售商提供的工具来帮助他们以及他们的公司避开一些与 production相关的问题.这些工具包括: 一.SQL*Plus SQL*Plus是SQL命令行和PL/SQL语言的界面和连接Oracle数据库客户和服务器的报告工具.SQL*Plus能 够交替使用并通过脚本运行.据Oracle公司表示,SQL*Plus以前

LINQ体验(8)—LINQ to SQL语句之Union All/Union/Intersect和Top/Bottom和Pag

LINQ体验(8)-LINQ to SQL语句之Union All/Union/Intersect和Top/Bottom和Paging和SqlMethods 我们继续讲解LINQ to SQL语句,这篇我们来讨论Union All/Union/Intersect操作和Top/Bottom操作和Paging操作和SqlMethods操作 . Union All/Union/Intersect操作 适用场景:对两个集合的处理,例如追加.合并.取相同项.相交项等等. Concat(连接) 说明:连接不