要将子查询放入圆括号中。
子查询可出现在WHERE子句、FROM子句、SELECT列表(此处(SELECT)只能是一个单行子查询)和HAVING子句,DDL,DML中。
子查询不能出现在主查询的GROUP BY语句中
子查询和主查询可以使用不同表,只要子查询返回的结果能够被主查询使用即可。
单行子查询只能使用单行操作符,多行子查询只能使用多行操作符。
在多行子查询中,ALL和ANY操作符不能单独使用,而只能与单行比较符(=、<、>、<=、>=、<>)结合使用。
要注意子查询中的空值问题。如果子查询返回了一个空值,则主查询将不会查询任何结果。
在WHERE子句中进行子查询的时候,不能带有GROUP BY子句。
子查询允许嵌套多层,但不能超过255层。
-- 同一张表中
-- 任务一:查询与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');
-- 任务四:查询出各部门员工的平均工资低于各部门最高平均工资的部门号和部门的平均工资。
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中允许聚合函数直接嵌套。
-- 任务一. 列出薪金与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);
-- 任务四 在emp表中,查询工资大于部门编号为10的任意一个员工工资的其他部门的员工信息。
SELECT ename,sal
FROM emp
WHERE sal >ANY(SELECT sal
FROM emp
WHERE deptno=10) AND deptno != 10;
-- 任务五 显示工资大于所有部门平均工资的雇员姓名,工资。
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);
-- 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;
-- 任务 查询显示和ALLEN同部门同职位的员工姓名、职位、部门编号
SELECT ename,job,deptno
FROM emp
WHERE (deptno,job)=(SELECT deptno,job
FROM emp
WHERE ename='ALLEN');
- 在单行子查询中和多行子查询中,内查询和外查询是分开执行的,也就是说内查询的执行与外查询的执行 没有关系,外查询仅仅是使用内查询的最终结果。
- 但是关联子查询中内查询与外查询是相互关联的。
- 内查询的执行需要借助于外查询,而外查询的执行又离不开内查询的执行。
-- 任务一 在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);
-- 任务一 创建一个和部门表一样的表,结构与数据都一样。
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 是随机给的,可以写其他的
-- 任务 统计出有奖金和没有奖金的人数
-- DISTINCT 去重
SELECT DISTINCT (SELECT COUNT(comm) FROM emp WHERE comm <> 0) 有奖金的人数,
(SELECT COUNT(*) FROM emp WHERE comm IS NULL OR comm = 0) 没有奖金的人数
FROM emp;
-- 任务一 将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;
-- 任务一 查询高于部门平均工资的员工信息
-- 将子查询放到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;
-- 任务一:查询工资大于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(单纯的合并查询结果,不处理重复数即不去重)