Oracle-SQL开发—— 子查询

章节目标

通过本章学习,应达到如下目标:

掌握子查询可以解决的问题;

了解子查询的分类;

掌握单行子查询、多行子查询、多列子查询;

掌握在WHERE、HAVING、FROM子句中编写子查询;

理解子查询返回空值对主查询所产生的影响;

掌握T0P-N及分页查询;

本章内容

为什么使用子查询

思考如下问题?

查询工资比Jones工资高的员工信息?

查询工资最低的员工姓名?

为什么使用子查询

“谁的薪水比 Jones还高呢?”

使用子查询

SELECT  select_list
FROM    table
WHERE   expr operator
            (SELECT select_list
            FROM        table);

括号内的查询叫做子查询,也叫内部查询,先于主查询执行。
子查询的结果被主查询(外部查询)使用
expr operator包括比较运算符。
单行运算符:>、=、>=、<、<>、<=
多行运算符: IN、ANY、ALL

使用子查询

子查询可以嵌于以下SQL子句中:
WHERE子句
HAVING子句
FROM子句

使用子查询

使用子查询

查询出比JONES的雇员工资高的其他雇员

SQL> SELECT ename
2  FROM   emp
3  WHERE  sal > 
4           (SELECT sal
5               FROM   emp
6               WHERE  ename='JONES');


ENAME
----------
KING
FORD
SCOTT

使用子查询

子查询使用指导

子查询要用括号括起来
将子查询放在比较运算符的右边
对于单行子查询要使用单行运算符
对于多行子查询要使用多行运算符

子查询的类型

根据子查询返回的行和列数量,分为:

单行子查询

子查询只返回一行一列

使用单行运算符

显示和雇员7369从事相同工作并且工资大于雇员7876的雇员的姓名和工作。

子查询中使用组函数

查询工资最低的员工姓名,岗位及工资


HAVING子句中使用子查询

查询部门最低工资比20部门最低工资高的部门编号及最低工资


HAVING子句中使用子查询

查询哪个部门的员工人数 高于各部门平均人数。

SQL> SELECT deptno, COUNT(empno)
2  FROM emp
3  GROUP BY deptno
4  HAVING   COUNT(empno) >
5        (SELECT AVG(COUNT(empno))
6         FROM  emp
7         GROUP BY deptno);

这个语句错在哪?


这条语句会工作吗?


练习1

  1. 查询入职日期最早的员工姓名,入职日期

  2. 查询工资比SMITH工资高并且工作地点在CHICAGO的员工姓名,工资,部门名称

  3. 查询入职日期比20部门入职日期最早的员工还要早的员工姓名,入职日期

  4. 查询部门人数大于所有部门平均人数的的部门编号,部门名称,部门人数

多行子查询

多行子查询

子查询返回记录的条数 可以是一条或多条。
和多行子查询进行比较时,需要使用多行操作符,多行操作符包括:
IN
ANY
ALL

IN操作符和以前介绍的功能一致,判断是否与子查询的任意一个返回值相同。

IN使用

SELECT empno,ename
FROM   emp
WHERE  sal =
                (SELECT   MIN(sal)
                FROM     emp
                GROUP BY deptno);

返回结果

ERROR at line 4:

ORA-01427: single-row subquery returns more than�one row

查询是经理的员工姓名,工资

SELECT  ename, sal
FROM    emp
WHERE   empno IN (SELECT mgr
                FROM  emp);

ANY的使用

ANY:表示和子查询的任意一行结果进行比较,有一个满足条件即可。
< ANY:表示小于子查询结果集中的任意一个,即小于最大值就可以。

> ANY:表示大于子查询结果集中的任意一个,即大于最小值就可以。

= ANY:表示等于子查询结果中的任意一个,即等于谁都可以,相当于IN。

查询是经理的员工姓名,工资。

SELECT  ename, sal
FROM    emp
WHERE   empno = ANY (SELECT mgr
                FROM   emp);

查询部门编号不为10,且工资比10部门任意一名员工工资高的员工编号,姓名,职位,工资。

SELECT empno, ename, job, sal
FROM   emp
WHERE  sal > ANY (SELECT sal
                FROM   emp
                WHERE  deptno = 10)
AND    deptno <> 10;

查询部门编号不为10,且工资比10部门任意一名工资低的员工编号,姓名,职位,工资。

SELECT empno, ename, job, sal
FROM   emp
WHERE  sal < ANY (SELECT sal
                FROM   emp
                WHERE  deptno = 10)
AND    deptno <> 10;

ALL的使用

ALL:表示和子查询的所有行结果进行比较,每一行必须都满足条件。
< ALL:表示小于子查询结果集中的所有行,即小于最小值。

> ALL:表示大于子查询结果集中的所有行,即大于最大值。

= ALL :表示等于子查询结果集中的所有行,即等于所有值,通常无意义。

查询部门编号不为10,且工资比10部门所有员工工资高的员工编号,姓名,职位,工资。

SELECT empno, ename,job, sal
FROM    emp
WHERE   sal > ALL (SELECT sal
                FROM   emp
                WHERE  deptno= 10)
AND    deptno <> 10;

查询部门编号不为10,且工资比10部门所有员工工资低的员工编号,姓名,职位,工资。

SELECT empno, ename,job, sal
FROM    emp
WHERE   sal < ALL (SELECT sal
                FROM   emp
                WHERE  deptno= 10)
AND    deptno <> 10;

查询部门编号不为10,且工资和10部门所有员工工资相等的员工编号,姓名,职位,工资。

SELECT empno, ename,job, sal
FROM    emp
WHERE   sal = ALL (SELECT sal
                FROM   emp
                WHERE  deptno= 10)
AND    deptno <> 10;

练习2

  1. 查询入职日期比10部门任意一个员工晚的员工姓名、入职日期,不包括10部门员工
  2. 查询入职日期比10部门所有员工晚的员工姓名、入职日期,不包括10部门员工
  3. 查询职位和10部门任意一个员工职位相同的员工姓名,职位,不包括10部门员工

多列子查询

多列子查询

之前讲的子查询都是在一个条件表达式内和子查询的一个列进行比较,多列子查询可以在一个条件表达式内同时和子查询的多个列进行比较。
多列子查询通常用IN操作符完成。

查询出和1981年入职的任意一个员工的部门和职位完全相同员工姓名、部门、职位、入职日期,不包括1981年入职员工。

SQL> SELECT ename, deptno, job, hiredate
2  FROM emp
3  WHERE   (deptno, job) IN
4       (SELECT deptno,job
5            FROM   emp
6            WHERE  to_char(hiredate,'YYYY')='1981')
7  AND      to_char(hiredate,'YYYY')<>'1981';

查询出和1981年入职的任意一个员工的部门或职位相同员工姓名、部门、职位、入职日期,不包括1981年入职员工。

SQL> SELECT ename, deptno, job, hiredate
2  FROM emp
3  WHERE    (deptno IN (SELECT deptno
4                      FROM     emp
5                      WHERE    to_char(hiredate,'YYYY')='1981')

6  OR     job IN             (SELECT    job
7               FROM    emp
8               WHERE   to_char(hiredate,'YYYY')='1981'))

9  AND     to_char(hiredate,'YYYY')<>'1981');

练习3

  1. 查询职位及经理和10部门任意一个员工职位及经理相同的员工姓名,职位,不包括10部门员工

  2. 查询职位及经理和10部门任意一个员工职位或经理相同的员工姓名,职位,不包括10部门员工

子查询中的空值

查询不是经理的员工姓名。

SQL> SELECT ename
2  FROM     emp
3  WHERE    empno NOT IN
4               (SELECT mgr
5                FROM   emp);
no rows selected.

子查询返回的结果中含有空值

上面的SQL语句试图查找出没有下属的雇员,逻辑上,这个SQL语句应该会返回8条记录,但是却一条也没返回,why?

因为子查询的结果中有一条空值,这条空值导致主查询没有记录返回。这是因为所有的条件和空值比较结果都是空值。因此无论什么时候只要空值有可能成为子查询结果集合中的一部分,就不能使用NOT IN 运算符。

在 FROM 子句中使用子查询

查询比自己部门平均工资高的员工姓名,工资,部门编号,部门平均工资

SQL> SELECT  a.ename, a.sal, a.deptno, b.salavg
2  FROM    emp a, (SELECT   deptno, avg(sal) salavg
3                  FROM     emp
4                  GROUP BY deptno) b
5  WHERE   a.deptno = b.deptno
6  AND     a.sal > b.salavg;

ENAME            SAL    DEPTNO     SALAVG
---------- --------- --------- ----------
KING            5000        10       2600
JONES           2975        20       2335
SCOTT           3000        20       2335
...
6 rows selected.

练习4

  1. 查询比自己职位平均工资高的员工姓名、职位,部门名称,职位平均工资

  2. 查询职位和经理同员工SCOTT或BLAKE完全相同的员工姓名、职位,不包括SCOOT和BLAKE本人。

  3. 查询不是经理的员工姓名。

ROWNUM

ROWNUM是一个伪列,伪列是使用上类似于表中的列,而实际并没有存储在表中的特殊列;
ROWNUM的功能是在每次查询时,返回结果集的顺序号,这个顺序号是在记录输出时才一步一步产生的,第一行显示为1,第二行为2,以此类推。

SQL> SELECT  rownum,empno, ename, job
2  FROM    emp;

    ROWNUM EMPNO ENAME      JOB
--------- ---------- ---------
    1     7839 KING       PRESIDENT
    2     7566 JONES      MANAGER
    3     7902 FORD       ANALYST
    4     7788 SCOTT      ANALYST

ROWNUM使用的注意点:

  1. 如下SQL语句,SELECT * FROM EMP WHERE ROWNUM>2;查询不到任何记录,因为ROWNUM是在记录输出时才生成,且总是从1开始,所以输出的第一条记录不满足>2的条件,被过滤掉,第二条的ROWNUM又成了1,又不满足〉2的条件,又被过滤掉,依此类推,所以永远没有满足条件的记录,返回为空。所以对于ROWNUM只能执行<、<=运算,不能执行>、>=或一个区间运算Between..And等
  2. ROWNUM和ORDER BY一起使用时,因为ROWNUM在记录输出时生成,而ORDER BY子句在最后执行,所以当两者一起使用时,需要注意ROWNUM实际是已经被排了序的ROWNUM。

TOP-N查询

Top-N查询主要是实现表中按照某个列排序,输出最大或最小的N条记录功能。
Top-N分析语法:

SELECT [列名], ROWNUM  
FROM   (SELECT [列名] 
        FROM 表名
        ORDER  BY Top-N操作的列  ASC|DESC)
WHERE  ROWNUM <=  N;

ASC:查询最小的N条记录

DESC:查询最大的N条记录

练习5

  1. 查询入职日期最早的前5名员工姓名,入职日期。

  2. 查询工作在CHICAGO并且入职日期最早的前2名员工姓名,入职日期。

分页

分页查询

在Oracle中,利用ROWNUM的特性,可以实现数据库端的分页查询,查询语法为:

  1. 当未指定需要按照某列排序,语法为:

    SELECT b.*
    FROM (SELECT ROWNUM rn,[列名1,列名2,....列名n]
    FROM 表名1,[表名2,...表名n]
    WHERE [条件表达式 AND ] ROWNUM <=目标页数每页记录数) b
    WHERE rn > (目标页数-1)
    每页记录数

    SELECT b.*
    FROM (SELECT ROWNUM rn,[列名1,列名2,....列名n]
    FROM 表名1,[表名2,...表名n]
    [WHERE 条件表达式]) b
    WHERE rn <=目标页数每页记录数 and rn > (目标页数-1)每页记录数
    思考:哪种方式效率高?

练习6

  1. 按照每页显示5条记录,分别查询第1页,第2页,第3页信息,要求显示员工姓名、入职日期、部门名称。

分页查询

  1. 当指定需要按照某列排序时,语法为:

    SELECT *
    FROM (SELECT ROWNUM rn, b.*
    FROM (SELECT 列名1 [,列名2,....列名n]
    FROM 表名1,[表名2,...表名n]
    [WHERE 子句]
    ORDER BY 要排序的列 ASC|DESC ) b
    WHERE ROWNUM <=目标页数每页记录数
    )
    WHERE rn > (目标页数-1)
    每页记录数 ;
    或:
    SELECT *
    FROM (SELECT ROWNUM rn, b.*
    FROM (SELECT 列名1 [,列名2,....列名n]
    FROM 表名1,[表名2,...表名n]
    [WHERE 子句]
    ORDER BY 要排序的列 ASC|DESC ) b
    )
    WHERE rn <=目标页数每页记录数 and rn > (目标页数-1)每页记录数;

练习7

  1. 按照每页显示5条记录,分别查询工资最高的第1页,第2页,第3页信息,要求显示员工姓名、入职日期、部门名称、工资。

本章重点总结

为什么使用子查询

单行子查询

多行子查询

多列子查询

子查询中空值问题

FROM语句中子查询

分页查询

课后作业

  1. 查询工资高于编号为7782的员工工资,并且和7369号员工从事相同工作的员工的编号、姓名及工资。

  2. 查询工资最高的员工姓名和工资。

  3. 查询部门最低工资高于10号部门最低工资的部门的编号、名称及部门最低工资。

  4. 查询员工工资为其部门最低工资的员工的编号和姓名及工资。

  5. 显示经理是KING的员工姓名,工资。

  6. 显示比员工SMITH参加工作时间晚的员工姓名,工资,参加工作时间。

  7. 使用子查询的方式查询哪些职员在NEW YORK工作。

  8. 写一个查询显示和员工SMITH工作在同一个部门的员工姓名,雇用日期,查询结果中排除SMITH。

  9. 写一个查询显示其工资比全体职员平均工资高的员工编号、姓名。

  10. 写一个查询显示其上级领导是King的员工姓名、工资。

  11. 显示所有工作在RESEARCH部门的员工姓名,职位。

  12. 查询每个部门的部门编号、平均工资,要求部门的平均工资高于部门20的平均工资。

  13. 查询大于自己部门平均工资的员工姓名,工资,所在部门平均工资,高于部门平均工资的额度。

  14. 列出至少有一个雇员的所有部门

  15. 列出薪金比"SMITH"多的所有雇员

  16. 列出入职日期早于其直接上级的所有雇员

  17. 找员工姓名和直接上级的名字

  18. 显示部门名称和人数

  19. 显示每个部门的最高工资的员工

  20. 显示出和员工号7369部门相同的员工姓名,工资

  21. 显示出和姓名中包含"W"的员工相同部门的员工姓名

  22. 显示出工资大于平均工资的员工姓名,工资

  23. 显示出工资大于本部门平均工资的员工姓名,工资

  24. 显示每位经理管理员工的最低工资,及最低工资者的姓名

  25. 显示比工资最高的员工参加工作时间晚的员工姓名,参加工作时间

  26. 显示出平均工资最高的的部门平均工资及部门名称


分割线


博主为咯学编程:父母不同意学编程,现已断绝关系;恋人不同意学编程,现已分手;亲戚不同意学编程,现已断绝来往;老板不同意学编程,现已失业三十年。。。。。。如果此博文有帮到你欢迎打赏,金额不限。。。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,980评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,178评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,868评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,498评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,492评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,521评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,910评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,569评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,793评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,559评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,639评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,342评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,931评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,904评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,144评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,833评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,350评论 2 342

推荐阅读更多精彩内容