Oracle查询中OVER (PARTITION BY ..)用法

为了方便大家学习和测试,所有的例子都是在Oracle自带用户Scott下建立的。

注:标题中的红色order by是说明在使用该方法的时候必须要带上order by。

一、rank()/dense_rank() over(partition by ...order by ...)

现在客户有这样一个需求,查询每个部门工资最高的雇员的信息,相信有一定oracle应用知识的同学都能写出下面的SQL语句:

select e.ename, e.job, e.sal, e.deptno
 from scott.emp e,
    (select e.deptno, max(e.sal) sal from scott.emp e group by e.deptno) me
 where e.deptno = me.deptno
  and e.sal = me.sal; 

在满足客户需求的同时,大家应该习惯性的思考一下是否还有别的方法。这个是肯定的,就是使用本小节标题中rank() over(partition by...)或dense_rank() over(partition by...)语法,SQL分别如下:

select e.ename, e.job, e.sal, e.deptno
 from (select e.ename,
        e.job,
        e.sal,
        e.deptno,
        rank() over(partition by e.deptno order by e.sal desc) rank
     from scott.emp e) e
 where e.rank = 1; 
select e.ename, e.job, e.sal, e.deptno
 from (select e.ename,
        e.job,
        e.sal,
        e.deptno,
        dense_rank() over(partition by e.deptno order by e.sal desc) rank
     from scott.emp e) e
 where e.rank = 1; 

为什么会得出跟上面的语句一样的结果呢?这里补充讲解一下rank()/dense_rank() over(partition by e.deptno order by e.sal desc)语法。

over: 在什么条件之上。

partition by e.deptno: 按部门编号划分(分区)。

order by e.sal desc: 按工资从高到低排序(使用rank()/dense_rank() 时,必须要带order by否则非法)

rank()/dense_rank(): 分级

整个语句的意思就是:在按部门划分的基础上,按工资从高到低对雇员进行分级,“级别”由从小到大的数字表示(最小值一定为1)。

那么rank()和dense_rank()有什么区别呢?

rank(): 跳跃排序,如果有两个第一级时,接下来就是第三级。

dense_rank(): 连续排序,如果有两个第一级时,接下来仍然是第二级。

小作业:查询部门最低工资的雇员信息。

二、min()/max() over(partition by ...)

现在我们已经查询得到了部门最高/最低工资,客户需求又来了,查询雇员信息的同时算出雇员工资与部门最高/最低工资的差额。这个还是比较简单,在第一节的groupby语句的基础上进行修改如下:

select e.ename,
     e.job,
     e.sal,
     e.deptno,
     e.sal - me.min_sal diff_min_sal,
     me.max_sal - e.sal diff_max_sal
  from scott.emp e,
     (select e.deptno, min(e.sal) min_sal, max(e.sal) max_sal
      from scott.emp e
      group by e.deptno) me
  where e.deptno = me.deptno
  order by e.deptno, e.sal;

上面我们用到了min()和max(),前者求最小值,后者求最大值。如果这两个方法配合over(partition by ...)使用会是什么效果呢?大家看看下面的SQL语句:

select e.ename,
    e.job,
    e.sal,
    e.deptno,
    nvl(e.sal - min(e.sal) over(partition by e.deptno), 0) diff_min_sal,
    nvl(max(e.sal) over(partition by e.deptno) - e.sal, 0) diff_max_sal
 from scott.emp e;

这两个语句的查询结果是一样的,大家可以看到min()和max()实际上求的还是最小值和最大值,只不过是在partition by分区基础上的。

小作业:如果在本例中加上order by,会得到什么结果呢?

三、lead()/lag() over(partition by ... order by ...)

中国人爱攀比,好面子,闻名世界。客户更是好这一口,在和最高/最低工资比较完之后还觉得不过瘾,这次就提出了一个比较变态的需求,计算个人工资与比自己高一位/低一位工资的差额。这个需求确实让我很是为难,在groupby语句中不知道应该怎么去实现。不过。。。。现在我们有了over(partition by ...),一切看起来是那么的简单。如下:

select e.ename,
    e.job,
    e.sal,
    e.deptno,
    lead(e.sal, 1, 0) over(partition by e.deptno order by e.sal) lead_sal,
    lag(e.sal, 1, 0) over(partition by e.deptno order by e.sal) lag_sal,
    nvl(lead(e.sal) over(partition by e.deptno order by e.sal) - e.sal,
      0) diff_lead_sal,
    nvl(e.sal - lag(e.sal) over(partition by e.deptno order by e.sal), 0) diff_lag_sal
 from scott.emp e; 

看了上面的语句后,大家是否也会觉得虚惊一场呢(惊出一身冷汗后突然鸡冻起来,这样容易感冒)?我们还是来讲解一下上面用到的两个新方法吧。

lead(列名,n,m): 当前记录后面第n行记录的<列名>的值,没有则默认值为m;如果不带参数n,m,则查找当前记录后面第一行的记录<列名>的值,没有则默认值为null。

lag(列名,n,m): 当前记录前面第n行记录的<列名>的值,没有则默认值为m;如果不带参数n,m,则查找当前记录前面第一行的记录<列名>的值,没有则默认值为null。

下面再列举一些常用的方法在该语法中的应用(注:带order by子句的方法说明在使用该方法的时候必须要带order by):

select e.ename,
    e.job,
    e.sal,
    e.deptno,
    first_value(e.sal) over(partition by e.deptno) first_sal,
    last_value(e.sal) over(partition by e.deptno) last_sal,
    sum(e.sal) over(partition by e.deptno) sum_sal,
    avg(e.sal) over(partition by e.deptno) avg_sal,
    count(e.sal) over(partition by e.deptno) count_num,
    row_number() over(partition by e.deptno order by e.sal) row_num
 from scott.emp e; 

大家在读完本片文章之后可能会有点误解,就是OVER (PARTITION BY ..)比GROUP BY更好,实际并非如此,前者不可能替代后者,而且在执行效率上前者也没有后者高,只是前者提供了更多的功能而已,所以希望大家在使用中要根据需求情况进行选择。

(0)

相关推荐

  • Oracle查询中OVER (PARTITION BY ..)用法

    为了方便大家学习和测试,所有的例子都是在Oracle自带用户Scott下建立的. 注:标题中的红色order by是说明在使用该方法的时候必须要带上order by. 一.rank()/dense_rank() over(partition by ...order by ...) 现在客户有这样一个需求,查询每个部门工资最高的雇员的信息,相信有一定oracle应用知识的同学都能写出下面的SQL语句: select e.ename, e.job, e.sal, e.deptno from scot

  • oracle数据库中sql%notfound的用法详解

    SQL%NOTFOUND 是一个布尔值.与最近的sql语句(update,insert,delete,select)发生交互,当最近的一条sql语句没有涉及任何行的时候,则返回true.否则返回false.这样的语句在实际应用中,是非常有用的.例如要update一行数据时,如果没有找到,就可以作相应操作.如: begin update table_name set salary = 10000 where emp_id = 10; if sql%notfound then insert into

  • Oracle查询语句中rownum与rowid的不同之处分析

    本文主要是以实例形式介绍了Oracle查询中rownum与rowid的不同之处,以及以假设的方式为例,查询条件为rownum = 2,在查询出第一条记录时的具体内容的介绍. 在查询中,我们可以注意到,类似于 select xx from table where rownum < n (n>1) 这样的查询是有正确含义的,而 select xx from table where rownum = n 这样的查询只在n=1的时候成立, select xx from table where rown

  • Oracle中游标Cursor基本用法详解

    查询 SELECT语句用于从数据库中查询数据,当在PL/SQL中使用SELECT语句时,要与INTO子句一起使用,查询的 返回值被赋予INTO子句中的变量,变量的声明是在DELCARE中.SELECT INTO语法如下: SELECT [DISTICT|ALL]{*|column[,column,...]} INTO (variable[,variable,...] |record) FROM {table|(sub-query)}[alias] WHERE............ PL/SQL

  • Oracle中ROW_NUMBER()OVER()函数用法实例讲解

    目录 1. 说明: 2. 原理: 3.语法: 4.示例一: 5. 示例二 总结 Oracle中ROW_NUMBER() OVER()函数用法 1. 说明: ROW_NUMBER() OVER() 函数的作用:分组排序 2. 原理: row_number() over() 函数,over() 里的分组以及排序的执行晚于 where.group by.order by 的执行. 3.语法: row_number() over( partition by 分组列 order by 排序列 desc )

  • java 查询oracle数据库所有表DatabaseMetaData的用法(详解)

    一 . 得到这个对象的实例 Connection con ; con = DriverManager.getConnection(url,userName,password); DatabaseMetaData dbmd = con.getMetaData(); 二. 方法getTables的用法 原型: ResultSet DatabaseMetaData.getTables(String catalog,String schema,String tableName,String []type

  • oracle中add_months()函数及用法总结

    今天对add_months函数进行简单总结一下: add_months 函数主要是对日期函数进行操作,在数据查询的过程中进行日期的按月增加,其形式为: add_months(date,int);其中第一个参数为日期,第二个为按月增加的幅度,例如: add_months (sysdate,2):就是当前日期的两个月之后的时间. 如:表示2个月以后的时间: SQL> select add_months(sysdate,2) from dual; ADD_MONTHS(SYSDATE,+2) ----

  • Oracle 中Contains 函数的用法

    1. 查询住址在北京的学生 SELECT student_id,student_name FROM students WHERE CONTAINS( address, 'beijing' ) remark: beijing是一个单词,要用单引号括起来. 2. 查询住址在河北省的学生 SELECT student_id,student_nameFROM students WHERE CONTAINS( address, '"HEIBEI province"' ) remark: HEBE

  • ORACLE数据库中Rownum用法详解

    ORACLE 中ROWNUM用法总结!  对于 Oracle 的 rownum 问题,很多资料都说不支持>,>=,=,between...and,只能用以上符号(<.<=.!=),并非说用>,& gt;=,=,between..and 时会提示SQL语法错误,而是经常是查不出一条记录来,还会出现似乎是莫名其妙的结果来,其实您只要理解好了这个 rownum 伪列的意义就不应该感到惊奇,同样是伪列,rownum 与 rowid 可有些不一样,下面以例子说明: 假设某个表

  • Oracle中PL/SQL的用法总结

    目录 一.PLSQL基本结构 1.基本数据类型变量 1. 基本数据类型 2. 基本数据类型变量的定义方法 2.表达式 1. 数值表达式 2. 字符表达式 3. 关系表达式 4. 逻辑表达式 3.PLSQL函数 4.系统输出打印 5.PL/SQL中的变量声明 二.变量赋值方式 三.PLSQL复合类型 1.记录类型record 2.表类型变量table 1. 定义一维表类型变量 2. 定义多维表类型变量 四. %type和%rowtype 五.PLSQL变量的可见空间 六.PLSQL流程控制 if判

随机推荐