EMP表是Oracle测试账户SCOTT中的一张雇员表,首先,我们来看看emp表的数据

  1. SQL> select * from emp;
  2.  
  3. EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO
  4. ----- ---------- --------- ---------- --------- ---------- ---------- ----------
  5. 7369 SMITH CLERK 7902 17-DEC-80 800 20
  6. 7499 ALLEN SALESMAN 7698 20-FEB-81 1600 300 30
  7. 7521 WARD SALESMAN 7698 22-FEB-81 1250 500 30
  8. 7566 JONES MANAGER 7839 02-APR-81 2975 20
  9. 7654 MARTIN SALESMAN 7698 28-SEP-81 1250 1400 30
  10. 7698 BLAKE MANAGER 7839 01-MAY-81 2850 30
  11. 7782 CLARK MANAGER 7839 09-JUN-81 2450 10
  12. 7788 SCOTT ANALYST 7566 19-APR-87 3000 20
  13. 7839 KING PRESIDENT 17-NOV-81 5000 10
  14. 7844 TURNER SALESMAN 7698 08-SEP-81 1500 0 30
  15. 7876 ADAMS CLERK 7788 23-MAY-87 1100 20
  16. 7900 JAMES CLERK 7698 03-DEC-81 950 30
  17. 7902 FORD ANALYST 7566 03-DEC-81 3000 20
  18. 7934 MILLER CLERK 7782 23-JAN-82 1300 10
  19.  
  20. 14 rows selected.

其中,empno是员工编号,同时也是该表的主键,ename是员工姓名,sal是员工工资,deptno是员工部门。

如何找出每个部门的最高工资的员工信息呢?

常用的方法是关联查询,SQL语句如下:

  1. select emp.deptno,ename,sal
  2. from emp,
  3. (select deptno,max(sal)maxsal from emp group by deptno) t
  4. where emp.deptno=t.deptno and emp.sal=t.maxsal;

结果如下:

  1. DEPTNO ENAME SAL
  2. ---------- ---------- ----------
  3. 30 BLAKE 2850
  4. 20 SCOTT 3000
  5. 10 KING 5000
  6. 20 FORD 3000

下面我们来看看执行计划:

  1. Execution Plan
  2. ----------------------------------------------------------
  3. Plan hash value: 269884559
  4.  
  5. -----------------------------------------------------------------------------
  6. | Id | Operation | Name | Rows | Bytes | Cost (%CPU) | Time |
  7. -----------------------------------------------------------------------------
  8. | 0 | SELECT STATEMENT | | 3 | 117 | 7 (15)| 00:00:01 |
  9. |* 1 | HASH JOIN | | 3 | 117 | 7 (15)| 00:00:01 |
  10. | 2 | VIEW | | 3 | 78 | 4 (25)| 00:00:01 |
  11. | 3 | HASH GROUP BY | | 3 | 21 | 4 (25)| 00:00:01 |
  12. | 4 | TABLE ACCESS FULL| EMP | 14 | 98 | 3 (0)| 00:00:01 |
  13. | 5 | TABLE ACCESS FULL | EMP | 14 | 182 | 3 (0)| 00:00:01 |
  14. -----------------------------------------------------------------------------
  15.  
  16. Predicate Information (identified by operation id):
  17. ---------------------------------------------------
  18.  
  19. 1 - access("EMP"."DEPTNO"="T"."DEPTNO" AND "EMP"."SAL"="T"."MAXSAL")
  20.  
  21. Statistics
  22. ----------------------------------------------------------
  23. 0 recursive calls
  24. 0 db block gets
  25. 13 consistent gets
  26. 0 physical reads
  27. 0 redo size
  28. 625 bytes sent via SQL*Net to client
  29. 419 bytes received via SQL*Net from client
  30. 2 SQL*Net roundtrips to/from client
  31. 0 sorts (memory)
  32. 0 sorts (disk)
  33. 4 rows processed

不难看出,该查询针对同一个表走了两次全盘扫描,成本为7,逻辑读为13。

如何对上述查询进行优化呢?在这里,用到分析函数LAST_VALUE,LAST_VALUE返回排序集中的最后一个值。

  1. SELECT deptno,ename,sal,
  2. LAST_VALUE(sal)
  3. OVER(PARTITION BY deptno
  4. ORDER BY sal
  5. ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)maxsal
  6. FROM emp;

输出结果如下:

  1. DEPTNO ENAME SAL MAXSAL
  2. ---------- ---------- ---------- ----------
  3. 10 MILLER 1300 5000
  4. 10 CLARK 2450 5000
  5. 10 KING 5000 5000
  6. 20 SMITH 800 3000
  7. 20 ADAMS 1100 3000
  8. 20 JONES 2975 3000
  9. 20 SCOTT 3000 3000
  10. 20 FORD 3000 3000
  11. 30 JAMES 950 2850
  12. 30 MARTIN 1250 2850
  13. 30 WARD 1250 2850
  14. 30 TURNER 1500 2850
  15. 30 ALLEN 1600 2850
  16. 30 BLAKE 2850 2850
  17.  
  18. 14 rows selected.

不难看出,sal等于maxsal的行即为每个部门最高工资的员工,下面用嵌套子查询得到目标结果。

  1. SELECT deptno,ename,sal FROM (
  2. SELECT deptno,ename,sal,
  3. LAST_VALUE(sal)
  4. OVER(PARTITION BY deptno
  5. ORDER BY sal
  6. ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)maxsal
  7. FROM emp) WHERE sal=maxsal;

输出结果如下:

  1. DEPTNO ENAME SAL
  2. ---------- ---------- ----------
  3. 10 KING 5000
  4. 20 SCOTT 3000
  5. 20 FORD 3000
  6. 30 BLAKE 2850

下面我们来看看该语句的执行计划:

  1. Execution Plan
  2. ----------------------------------------------------------
  3. Plan hash value: 4130734685
  4.  
  5. ----------------------------------------------------------------------------
  6. | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
  7. ----------------------------------------------------------------------------
  8. | 0 | SELECT STATEMENT | | 14 | 644 | 4 (25)| 00:00:01 |
  9. |* 1 | VIEW | | 14 | 644 | 4 (25)| 00:00:01 |
  10. | 2 | WINDOW SORT | | 14 | 182 | 4 (25)| 00:00:01 |
  11. | 3 | TABLE ACCESS FULL | EMP | 14 | 182 | 3 (0)| 00:00:01 |
  12. ----------------------------------------------------------------------------
  13.  
  14. Predicate Information (identified by operation id):
  15. ---------------------------------------------------
  16.  
  17. 1 - filter("SAL"="MAXSAL")
  18.  
  19. Statistics
  20. ----------------------------------------------------------
  21. 0 recursive calls
  22. 0 db block gets
  23. 6 consistent gets
  24. 0 physical reads
  25. 0 redo size
  26. 619 bytes sent via SQL*Net to client
  27. 419 bytes received via SQL*Net from client
  28. 2 SQL*Net roundtrips to/from client
  29. 1 sorts (memory)
  30. 0 sorts (disk)
  31. 4 rows processed

可见,引入了分析函数以后,成本和逻辑读都减少了一半。

通过查询的结果,我们可以看出,20号部门有两个人的工资最高,有时候,我们只想得到一个人的信息,如何实现呢?

在这里我们会用到分析函数LAG,具体SQL如下:

  1. SELECT deptno,ename,sal,LAG(sal)OVER(ORDER BY deptno) presal FROM (
  2. SELECT deptno,ename,sal,
  3. LAST_VALUE(sal)
  4. OVER(PARTITION BY deptno
  5. ORDER BY sal
  6. ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)maxsal
  7. FROM emp) WHERE sal=maxsal;

输出结果如下:

  1. DEPTNO ENAME SAL PRESAL
  2. ---------- ---------- ---------- ----------
  3. 10 KING 5000
  4. 20 SCOTT 3000 5000
  5. 20 FORD 3000 3000
  6. 30 BLAKE 2850 3000

剔除sal等于presal的行

  1. SELECT deptno,ename,sal FROM (
  2. SELECT deptno,ename,sal,LAG(sal)OVER(ORDER BY deptno) presal FROM (
  3. SELECT deptno,ename,sal,
  4. LAST_VALUE(sal)
  5. OVER(PARTITION BY deptno
  6. ORDER BY sal
  7. ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)maxsal
  8. FROM emp)
  9. WHERE sal=maxsal) WHERE sal <> presal or presal is null;

输出结果如下:

  1. DEPTNO ENAME SAL
  2. ---------- ---------- ----------
  3. 10 KING 5000
  4. 20 SCOTT 3000
  5. 30 BLAKE 2850

总结:

在实际生产环境中,此类应用还是蛮多的,譬如如何查询每个时段耗时最大的工单。当然,通过上述演示,我们也看出了group by函数的局限性。

关于LAST_VALUE和LAG函数的具体应用及说明,可参考Oracle官方文档:

1. LAST_VALUE

2. LAG

如何用分析函数找出EMP表中每个部门工资最高的员工的更多相关文章

  1. 找出sql脚本中需要创建的表空间名称和数据库用户名

    测试的工作中,经常会遇到项目交接或者搭建一个新的测试环境,而创建oracle数据库用户及表空间时,需要提前找出脚本中的 数据库用户名和表空间名,所以自己写了一个python脚本,自动找出sql脚本中的 ...

  2. 转 A 、B两张表,找出ID字段中,存在A表,但是不存在B表的数据

    A.B两张表,找出ID字段中,存在A表,但是不存在B表的数据,A表总共13W数据,去重后大约3万条数据,B表有2W条数据,且B表的ID有索引. 方法一 使用not in,容易理解,效率低. selec ...

  3. ORACLE中如何找出大表分布在哪些数据文件中?

    ORACLE中如何找出大表分布在哪些数据文件中?   在ORACLE数据中,我们能否找出一个大表的段对象分布在哪些数据文件中呢? 答案是可以,我们可以用下面脚本来找出对应表的区.段分别位于哪些数据文件 ...

  4. Entity Framework 6 Recipes 2nd Edition(9-3)译->找出Web API中发生了什么变化

    9-3. 找出Web API中发生了什么变化 问题 想通过基于REST的Web API服务对数据库进行插入,删除和修改对象图,而不必为每个实体类编写单独的更新方法. 此外, 用EF6的Code Fri ...

  5. 一个N*M的矩阵,找出这个矩阵中所有元素的和不小于K的面积最小的子矩阵

    题目描述: 一个N*M的矩阵,找出这个矩阵中所有元素的和不小于K的面积最小的子矩阵(矩阵中元素个数为矩阵面积) 输入: 每个案例第一行三个正整数N,M<=100,表示矩阵大小,和一个整数K 接下 ...

  6. 找出Java进程中大量消耗CPU

    原文:https://github.com/oldratlee/useful-shells useful-shells 把平时有用的手动操作做成脚本,这样可以便捷的使用. show-busy-java ...

  7. 笔试题&amp;面试题:找出一个数组中第m小的值并输出

    题目:找出一个数组中第m小的值并输出. 代码: #include <stdio.h> int findm_min(int a[], int n, int m) //n代表数组长度,m代表找 ...

  8. 找出程序GasMileage中的哪一行与下列叙述相对应:

    找出程序GasMileage中的哪一行与下列叙述相对应: a.通知程序将使用Scanner类   import java.util.Scannner; b.创建一个Scanner类的对象   Scan ...

  9. 如何在EXCEL中找出第一列中不包含的第二列数据

    1.找出第一列中不包含的第二列数据:=IFERROR(VLOOKUP(A:A,B:B,1,0),"无") 2.A列相同,B列相加:=SUMIF(G:G,G1,J:J)

随机推荐

  1. 第七章 内存管理单元MMU介绍

    7.1 内存管理单元MMU介绍 7.1.1 S3C2410/S3C2440 MMU特性 负责虚拟地址到物理地址的映射,并提供硬件机制的内存访问权限检查 特性: 与ARM V4兼容的映射长度.域.访问权 ...

  2. PHP如何将中文转换为拼音

    用来得到中文的首字母: 这个是将中文转换为拼音的类:charset <?php/*** 汉字转化为拼音,拼音转化为汉字**/ class charset{private $_code=array ...

  3. 前端html、Javascript、CSS技术小结

    简单地总结了一下前端用过的html.javascript.css技术,算是清点一下,做个大略的小结,为进一步的学习给个纲领. 一.HTML 由于HTML5的兴起,简单地判断一个网页是否是html5网页 ...

  4. 【摘】【网络】无线AP与无线路由器有什么区别?

    参考网站: 1.无线上网百科 http://wifi.baike.com/article-290204.html 图片 1 今天我们从功能.应用.组网和成本四个方面为大家区分无线路由器和无线AP.当前 ...

  5. java中类型转换

    1.基本数据类型转换    char, byte,short,int,long,float,double,boolean (1)小类型数据可以直接赋给大类型数据          例:char a=' ...

  6. shell 知识

    解压 tar.bz2文件 bunzip2 linux-2.6.13.tar.bz2 | tar xvf -

  7. cefsharp开发实例1

    做了几年.NET开发,基本都是搞WEB居多,以前也搞过一个winform项目,虽然很把界面拼接出来了,但是感觉有点痛苦,改动的时候又要改动一大堆代码.最近又要搞个桌面软件,试着搜索了下html做界面方 ...

  8. Restful.Data,现招募有为骚年,群号 338570336

    光阴似箭,日月如梭,套用小学作文惯用的一句开场白来开始重新开始我的博客园生涯吧. 8年的风霜雪雨,不断的击打着我内心的哀伤,可我依旧坚挺的屹立在这里,是因为技术是我一直坚持的梦想. 追寻着先辈和高人的 ...

  9. 循序渐进做项目系列(1):最简单的C/S程序——让服务器来做加法

    (本文是专门针对未接触过C/S开发的初学者而写的,C/S开发高手请自动忽略啊~~) 还在写“Hello world!”式的单机程序吗?还在各种拖控件吗?是否自己都觉得有点low呢?来个质的飞跃吧!看看 ...

  10. Chrome扩展程序的二次开发:把它改得更适合自己使用

    我当然知道未经作者允许修改别人程序是不道德的了,但作为学习研究之用还是无可厚非,这里仅供交流. 一切都是需求驱动的 话说某天我在网上猎奇的时候无意间发现这么一款神奇的谷歌浏览器插件:Extension ...