子查询

  子查询本身并不属于一个新的概念,应该说它是将之前的所有查询进行了新的组合,在一个查询之中,包含了其他若干个小的查询,这样的查询就称为子查询,理论上可以在 SQL 语句任意位置上设置子查询,例如:给定的子查询操作语法。

SELECT [DISTINCT] 列 [别名] , 列 [别名] ,... | 统计函数 (
    SELECT [DISTINCT] 列 [别名] , 列 [别名] ,... | 统计函数
    FROM 表名称 [别名] , 表名称 [别名] , ...
    [WHERE 条件(s)]
    [GROUP BY 分组字段 , 分组字段 , 分组字段 ,...] 
    [HAVING 条件(s)]
    [ORDER BY 字段 [ASC | DESC] , 字段 [ASC | DESC] ,...])
FROM 表名称 [别名] , 表名称 [别名] , ...(
    SELECT [DISTINCT] 列 [别名] , 列 [别名] ,... | 统计函数
    FROM 表名称 [别名] , 表名称 [别名] , ...
    [WHERE 条件(s)]
    [GROUP BY 分组字段 , 分组字段 , 分组字段 ,...] 
    [HAVING 条件(s)]
    [ORDER BY 字段 [ASC | DESC] , 字段 [ASC | DESC] ,...])
[WHERE 条件(s) (
    SELECT [DISTINCT] 列 [别名] , 列 [别名] ,... | 统计函数
    FROM 表名称 [别名] , 表名称 [别名] , ...
    [WHERE 条件(s)]
    [GROUP BY 分组字段 , 分组字段 , 分组字段 ,...] 
    [HAVING 条件(s)]
    [ORDER BY 字段 [ASC | DESC] , 字段 [ASC | DESC] ,...])]
[GROUP BY 分组字段 , 分组字段 , 分组字段 ,...] 
[HAVING 条件(s) (
    SELECT [DISTINCT] 列 [别名] , 列 [别名] ,... | 统计函数
    FROM 表名称 [别名] , 表名称 [别名] , ...
    [WHERE 条件(s)]
    [GROUP BY 分组字段 , 分组字段 , 分组字段 ,...] 
    [HAVING 条件(s)]
    [ORDER BY 字段 [ASC | DESC] , 字段 [ASC | DESC] ,...])]
[ORDER BY 字段 [ASC | DESC] , 字段 [ASC | DESC] ,...] ;

  一般而言,子查询经常会在三个地方出现:WHERE 子句、FROM 子句、 FROM 子句,那么刚开始学习的时候,给出以下的一些不成文规定,以确定子查询出现位置:
  
   · WHERE 子句中出现:子查询返回 单行单列、单行多列、多行单列;
  
  · HAVING 子句中出现:子查询返回 单行单列,但同时都是为了使用统计函数;
  
  · FROM 子句中出现:返回多行多列子查询。

1.1、在WHERE 子句之中定义子查询

  WHERE 子句针对于所有的数据进行筛选,在之前,可以在 WHERE 子句之中判断某一个单独的数值,或者是判断某一个范围是否满足,实际上这些都可以和子查询进行连接。

1、子查询返回单行单列

范例: 要求查询出公司工资最低的雇员信息
  · 首先应该确定出公司的最低工资是多少,可以直接利用 MIN()函数计算

SELECT MIN(sal) FROM emp;

这里写图片描述

  · 以上的查询返回了单行单列,那么可定在 WHERE 子句之中使用,同时还要考虑,肯定有员工的工资为以上查询的结果,可以利用 “=” 判断;

SELECT * FROM emp
WHERE sal=(
    SELECT MIN(sal) FROM emp);

这里写图片描述

范例: 要求查询出低于公司平均工资的全部雇员信息

  · 首先应该查询出公司的平均工资,使用 AVG() 函数。

SELECT AVG(sal) FROM emp;

这里写图片描述

  · 比以上查询结果低的工资的雇员信息要进行显示。

SELECT * FROM emp
WHERE sal<(
    SELECT AVG(sal) FROM emp);

这里写图片描述

2、子查询返回单行多列(一般少见)

范例: 要求查询出与 SCOTT 从事同一工作,并且工资相同的雇员信息。

  · 查询出 SCOTT 工作与工资, 返回单行多列。

SELECT job,sal FROM emp WHERE ename='SCOTT';

这里写图片描述

  · 两个内容都要相同,直接在 WHERE 子句中进行判断:

SELECT * FROM emp 
WHERE (job,sal)=(
    SELECT job,sal FROM emp WHERE ename='SCOTT')
    AND ename<>'SCOTT';

这里写图片描述

3、子查询返回多行单列
  
  如果子查询返回的数据是多行多列,那么意味着,返回的是一组范围,那么如果要想进行范围判断,则可以使用三类符号完成:IN、ANY、ALL,下面分别来看:

(1)、IN:满足指定范围

SELECT * FROM emp
WHERE sal IN (SELECT sal FROM emp WHERE job='MANAGER');

这里写图片描述

  最早使用IN的时候是由用户自己设置的范围,但是现在的范围是通过子查询取得的,同样,如果使用的是 NOT IN 表示不在此范围之中。

SELECT * FROM emp
WHERE sal NOT IN (SELECT sal FROM emp WHERE job='MANAGER');

这里写图片描述
  
  而同时还有一点需要注意的是,在使用 NOT IN 操作的过程中,子查询是不能够存在 NULL。
  
(2)、ANY:此操作符有三种表现形式:

  · =ANY :与 IN 操作符功能一致

SELECT * FROM emp
WHERE sal NOT IN (SELECT sal FROM emp WHERE job='MANAGER');

这里写图片描述

  · >ANY :比子查询之中返回的最小的数据要大

SELECT * FROM emp
WHERE sal >ANY(SELECT sal FROM emp WHERE job='MANAGER');

这里写图片描述

  · < ANY :比子查询之中返回的最大的数据要小

SELECT * FROM emp
WHERE sal <ANY(SELECT sal FROM emp WHERE job='MANAGER');

这里写图片描述

(3)、ALL:此操作符有两种表现形式:

  · > ALL :比子查询之中返回的最大的数据要大

SELECT * FROM emp
WHERE sal >ALL(SELECT sal FROM emp WHERE job='MANAGER');

这里写图片描述

  · < ANY :比子查询之中返回的最小的数据要小

SELECT * FROM emp
WHERE sal <ALL(SELECT sal FROM emp WHERE job='MANAGER');

这里写图片描述

  至于以上三个操作符使用哪一种,完全由你的查询需求来决定。

1.2、在HAVING 子句之中定义子查询

  在 HAVING 子句之中出现子查询只会返回单行单列,并且会在使用统计函数的情况下出现此类情况。

范例: 要求查询出比公司平均工资还要高的部门编号、部门人数、平均工资。
  • 首先应该知道公司的平均工资,返回单行单列。

SELECT AVG(sal) FROM emp;

  • 下面应该按照部门编号分组,同时设置一个分组后的过滤条件,HAVING子句。

SELECT deptno,COUNT(empno),AVG(sal)
FROM emp
GROUP BY deptno 
HAVING AVG(sal)>(
    SELECT AVG(sal) FROM emp);

这里写图片描述

1.3、在FROM 子句之中定义子查询

  
  FROM 子句的主要功能是却定数据的来源,如果说现在在一个子查询返回的数据形式是多行多列,这实际上就是一个标的结构,但是这确实一张临时表,可是 SQL 不管实体表还是临时表,只要是多行多列就会按照表进行读取,所以子查询返回多行多列时,就是一张临时的数据表,必须在 FROM 子句之中出现。

范例: 要求查询出每个部门的编号、名称、位置、部门人数、平均工资

实现方式一: 本题目的要求在之前可以通过多字段分组求出,直接利用 GROUP BY 完成,并且结合多表查询。

SELECT d.deptno,d.dname,d.loc,COUNT(e.empno),AVG(e.sal)
FROM emp e,dept d
WHERE e.deptno(+)=d.deptno
GROUP BY d.deptno,d.dname,d.loc;

实现方式二: 利用子查询完成

  • 首先现在先查询出部门的完整信息,这个时候只需要依靠dept表即可。

SELECT d.deptno,d.dname,d.loc 
FROM dept d;

  • 之后查询出每个部门编号、部门人数、部门平均工资,只需要emp表即可。

SELECT deptno,COUNT(empno),AVG(sal) 
FROM emp 
GROUP BY deptno;

  • 统计查询的结果返回的是多行多列,而且也存在了 deptno 字段的信息,那么能否把前面两个查询变为一个?。

SELECT d.deptno,d.dname,d.loc,temp.count,temp.avg  
FROM dept d, (
    SELECT deptno,COUNT(empno) count,AVG(sal) avg
    FROM emp 
    GROUP BY deptno) temp
WHERE d.deptno=temp.deptno(+);

这里写图片描述


1.4、性能分析

   那么现在就会出现一个问题了,两种实现方案都可以实现同样的功能,使用那种更好呢?为了分析这两类的操作,现在可以将emp或dept表的数据量假设扩大100倍,即:emp表的数据量是1400条,dept表的数据量是400条。
  
   • 方式一的数据量:采用的是多表查询,而后进行多字段的分组方式,如果要使用多表查询,那么就一定会出现笛卡尔积,那么此时的操作的数据量是:emp表的1400条 * dept表的400条 = 560000 条
  
   • 方式二的数据量:采用子查询,所以数据量要分开统计:
  
    |- 子查询的数据量:emp表的1400条数据,子查询最多返回400行记录;
    
    |- 将dept表与子查询进行多表连接:dept表的400条 * 子查询最多的400条 = 160000条;
   
    |- 最终的总量:子查询的1400条 + 外部查询的160000条记录 = 161400条记录。

  很明显,子查询的性能绝对要高于多表查询的性能,即:在任何的实际开发之中,子查询是解决多表查询中出现性能问题的唯一方式。(或者设置冗余字段)

  
  在FROM子句之中出现子查询还有一个原因:外部查询需要使用统计函数操作,但是整体查询又无法实现分组统计操作的情况下使用。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值