子查询的使用及相关知识点

子查询

理解并口述
1. 什么是子查询
  • 子查询是指插入在其他SQL语句中的SELECT语句,也称为嵌套查询。 使用子查询主要是将结果作为外部主查询的查询条件来使用的查询。
2. 什么时候使用子查询
  1. 当要显示的数据表里并不存在,但可以通过对已有的数据加工或得,可通过子查询实现
  2. 子查询可以出现在SELECT 、WHERE子句、FROM子句、DML语句、DDL与中
  3. 在SELECT、INSERT、UPDATE或DELETE命令中允许是一个表达式的地方都可以包含子查询,子查询甚至可以包含在另外一个子查询中。
3. 子查询编写思路
  1. 仔细分析题目,确定要查询的表及字段(数据)
  2. 分析要查询的字段(数据)哪些在表里直接存在,哪些不存在
  3. 考虑如何把要显示的数据造出来(通过查询语句获得)
  4. 考虑子查询与表的连接点是什么(通常是主外键,共有字段)
  5. 考虑子查询要放在什么位置
  6. 组合成完整的SQL语句
4. 使用子查询的注意事项
  1. 要将子查询放入圆括号中。

  2. 子查询可出现在WHERE子句、FROM子句、SELECT列表(此处(SELECT)只能是一个单行子查询)和HAVING子句,DDL,DML中。

  3. 子查询不能出现在主查询的GROUP BY语句中

  4. 子查询和主查询可以使用不同表,只要子查询返回的结果能够被主查询使用即可。

  5. 单行子查询只能使用单行操作符,多行子查询只能使用多行操作符。

  6. 在多行子查询中,ALL和ANY操作符不能单独使用,而只能与单行比较符(=、<、>、<=、>=、<>)结合使用。

  7. 要注意子查询中的空值问题。如果子查询返回了一个空值,则主查询将不会查询任何结果。

  8. 在WHERE子句中进行子查询的时候,不能带有GROUP BY子句。

  9. 子查询允许嵌套多层,但不能超过255层。

5. 常规SQL语句优化
  1. 建议不用"*"来代替所有列名
  2. 用TRUNCATE代替delete(删除数据表中所有数据时)
  3. 在确保完整性的情况下多用COMMIT(事务提交)
  4. 尽量减少表的查询次数
  5. 用[NOT] EXISTS代替[NOT] IN
动手做
1. 单行子查询
(1) 在WHERE子句中使用单行子查询
-- 同一张表中
-- 任务一:查询与SCOTT在同一部门的员工的姓名,薪水
SELECT deptno FROM emp WHERE ename='SCOTT';  -- 先查询出来部门号
SELECT ename,sal FROM emp WHERE deptno=20;  -- 与SCOTT同一部门的员工信息
-- 子查询组合
SELECT ename,sal 
FROM emp 
WHERE deptno=(SELECT deptno 
              FROM emp 
              WHERE ename='SCOTT');   -- 查询结果与上面两条SQL语句相同

-- 任务二:查询出工资低于所有员工平均工资的员工姓名和工资,按照工资高低降序排序。
SELECT ename,sal
FROM emp
WHERE sal < (SELECT AVG(sal) 
             FROM emp)
ORDER BY sal DESC;


-- 不使用同一张表
-- 任务三: 查询部门名称为"RESEARCH"的员工信息(显示员工号,姓名,职位)
SELECT empno,ename,job
FROM emp
WHERE deptno=(SELECT deptno 
              FROM dept
              WHERE dname='RESEARCH');
(2) 在HAVING子句中使用单行子查询
-- 任务四:查询出各部门员工的平均工资低于各部门最高平均工资的部门号和部门的平均工资。
SELECT deptno,AVG(sal)
FROM emp
GROUP BY deptno
HAVING AVG(sal) <(SELECT MAX(e.avgSal)
                  FROM (SELECT AVG(sal) AS avgSal 
                        FROM emp 
                        GROUP BY deptno) e);

注意单行子查询中经常遇到的错误

  • 因为WHERE条件限定不规范而返回多行,就会出现单行子查询返回多行错误。
  • 子查询中不能包含ORDER BY子句,相反任何排序都必须在外部查询中完成。
  • MySQL中不允许出现聚合函数直接嵌套:例如MAX(AVG(sal)会报错,可以写成子查询。必须给出表的别名。Oracle中允许聚合函数直接嵌套。
2. 多行子查询
2.1 多行子查询中使用IN操作符
-- 任务一. 列出薪金与30号部门员工的薪金相同的所有员工的姓名和薪金。
SELECT ename,sal 
FROM emp 
WHERE sal IN (SELECT sal 
              FROM emp 
              WHERE deptno=30);

-- 标准嵌套子查询多层嵌套
-- 任务二. 列出薪水与销售部门(SALESMAN)在同部门的员工薪水相同的所有员工的姓名和薪金。
SELECT ename,sal
FROM emp
WHERE sal IN (SELECT sal 
              FROM emp 
              WHERE deptno=(SELECT deptno 
                            FROM dept
                            WHERE dname='SALES'));


-- 任务三. 列出至少有4个员工的所有部门信息
SELECT * 
FROM dept
WHERE deptno IN (SELECT deptno
                 FROM emp
                 GROUP BY deptno
                 HAVING COUNT(*) > 4);
2.2 多行子查询中使用ANY操作符
-- 任务四 在emp表中,查询工资大于部门编号为10的任意一个员工工资的其他部门的员工信息。
SELECT ename,sal
FROM emp
WHERE sal >ANY(SELECT sal
               FROM emp
               WHERE deptno=10) AND deptno != 10;
2.3 多行子查询中使用ALL操作符
-- 任务五 显示工资大于所有部门平均工资的雇员姓名,工资。

SELECT ename,sal
FROM emp
WHERE sal >ALL(SELECT AVG(sal)
               FROM emp
               GROUP BY deptno);

-- 任务六 查询工资大于部门编号为20的所有员工工资的员工信息
SELECT ename,sal
FROM emp
WHERE sal >ALL(SELECT sal 
               FROM emp
               WHERE deptno=20);
2.4 子查询中使用EXISTS操作符
-- EXISTS 运算符用于判断查询子句是否有记录,如果有一条或多条记录存在返回 True,否则返回 False
-- EXISTS里的查询内容如果存在,就输出,没有就不输出
-- 子查询不能单独查,必须和主查询一起使用(也就是关联子查询)
-- 任务七 查询在“SALES"销售部门的所有员工信息。
SELECT *
FROM emp e
WHERE EXISTS(SELECT deptno
             FROM dept d
             WHERE d.dname='SALES' AND e.deptno = d.deptno);  

-- 任务八 查询在NEW YORK工作的所有雇员的名字、职位、薪水、所在部门。
SELECT ename,job,sal,dname
FROM emp e,dept d
WHERE EXISTS(SELECT loc
             FROM dept d
             WHERE loc='NEW YORK' AND e.deptno=d.deptno) AND d.deptno=e.deptno;
3. 多列子查询
-- 任务 查询显示和ALLEN同部门同职位的员工姓名、职位、部门编号
SELECT ename,job,deptno
FROM emp 
WHERE (deptno,job)=(SELECT deptno,job 
                    FROM emp 
                    WHERE ename='ALLEN');
4. 关联子查询
  • 在单行子查询中和多行子查询中,内查询和外查询是分开执行的,也就是说内查询的执行与外查询的执行 没有关系,外查询仅仅是使用内查询的最终结果。
  • 但是关联子查询中内查询与外查询是相互关联的。
  • 内查询的执行需要借助于外查询,而外查询的执行又离不开内查询的执行。
-- 任务一 在emp表中,使用“关联子查询”检索工资大于同职位的平均工资的员工信息(显示字段:员工编号,姓名,工资)。
SELECT empno,ename,sal
FROM emp e
WHERE sal > (SELECT AVG(sal)
             FROM emp m
             WHERE m.job = e.job);

-- 任务二 查询所有大于本部门平均工资的员工信息
SELECT empno,ename,sal
FROM emp e
WHERE sal > (SELECT AVG(sal)
             FROM emp m
             WHERE e.deptno=m.deptno);
5. 在DDL的建表语句中使用子查询
-- 任务一 创建一个和部门表一样的表,结构与数据都一样。
CREATE TABLE dept1 AS SELECT * FROM dept; 
-- 任务二 创建一个和部门表结构一样的空表,没有数据。
-- WHERE 里给一个false的值,比如(1=2,1不等2,所以该值是false),给一个false的值才会返回空表
CREATE TABLE dept2 AS SELECT * FROM dept WHERE 1=2;  -- 1=2 是随机给的,可以写其他的
6. 在SELECT语句中使用子查询
-- 任务 统计出有奖金和没有奖金的人数
-- DISTINCT 去重
SELECT DISTINCT (SELECT COUNT(comm) FROM emp WHERE comm <> 0) 有奖金的人数,
       (SELECT COUNT(*) FROM emp WHERE comm IS NULL OR comm = 0) 没有奖金的人数
FROM emp;
7. 插入语句中使用子查询
-- 任务一 将20号部门的员工信息插入新的员工表emp1中 (emp1与emp结构一样,没有数据)
CREATE TABLE emp1 AS SELECT * FROM emp WHERE 1=2; -- 先创建emp1
INSERT INTO emp1 SELECT * FROM emp WHERE deptno=20;
SELECT * FROM emp1;
8. FROM子句中使用子查询
-- 任务一 查询高于部门平均工资的员工信息
-- 将子查询放到FROM后边当表,这就如果涉及到多表查询,那么子查询中查出来的列中就要有和其他表的列中有相同列,来当做连接条件。
-- 下面的就是查出来的表中有deptno,和emp表中的deptno可以进行连接
SELECT e.*
FROM emp e,(SELECT deptno, AVG(sal) avgSal
            FROM emp
            GROUP BY deptno) d
WHERE e.deptno=d.deptno AND e.sal > d.avgSal;

-- 任务二 检索部门编号、部门名称、部门所在地及其每个部门的员工总数。
SELECT d.deptno,dname,loc,e.num
FROM dept d LEFT JOIN(SELECT deptno,count(*) num
             FROM emp
             GROUP BY deptno) e
ON d.deptno = e.deptno;
9. 合并查询结果
-- 任务一:查询工资大于2500或者职位为经理的员工的姓名,工资,职位。
SELECT ename,sal,job FROM emp WHERE job='MANAGER'
UNION  -- 合并表(去重)
SELECT ename,sal,job FROM emp WHERE sal > 2500;

SELECT ename,sal,job FROM emp WHERE job='MANAGER'
UNION ALL  -- 合并表(没有去重)
SELECT ename,sal,job FROM emp WHERE sal > 2500;

注意:union(合并查询结果并去重);union all(单纯的合并查询结果,不处理重复数即不去重)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值