✨欢迎来到小K的MySQL专栏,本节将为大家带来MySQL标量/单行子查询、列子/表子查询的讲解✨
子查询指一个查询语句嵌套在另一个查询语句内部的查询,这个特性从
MySQL 4.1
开始引入;在特定情况下,一个查询语句的条件需要另一个查询语句来获取,内层查询(inner query
)语句的查询结果,可以为外层查询(outer query
)语句提供查询条件。
内层查询即子查询,外层查询即主查询,只是叫法不同而已
✨查询需求:
查询公司中工资最低的员工信息
工资最低是多少?
SELECT * FROM emp WHERE sal=(SELECT MIN(sal) FROM emp);
✨子查询规范:
SELECT
字句中(如:SELECT、FROM、WHERE、ORDER BY、HAVING子句
)✨子查询分类:
根据子查询返回的数据分类:
标量子查询(scalar subquery):返回1行1列一个值
行子查询(row subquery):返回的结果集是 1 行 N 列
列子查询(column subquery):返回的结果集是 N 行 1列
表子查询(table subquery):返回的结果集是 N 行 N 列
子查询可以返回一个标量(就一个值)、一个行、一个列或一个表,这些子查询分别称之为标量、行、列和表子查询。
根据子查询和主查询之间是否有条件关联分类:
为了方便,对于在何处使用子查询给大家给出几点个人建议:
- 子查询出现在WHERE子句中:此时子查询返回的结果一般都是单列单行、单行多列、多行单列
- 子查询出现在HAVING子句中:此时子查询返回的都是单行单列数据,同时为了使用统计函数操作
- 子查询出现在FROM子句中:此时子查询返回的结果图一般都是多行多列,可以按照一张数据表(临时表)的形式操作
✨✨标量子查询
子查询返回的是单行单列的数据,就是一个值
SELECT * FROM emp WHERE sal<(SELECT sal FROM emp WHERE eanme='ALLEN');
SELECT * FROM emp WHERE sal>(SELECT AVG(sal) FROM emp);
SELECT * FROM emp WHERE job=(SELECT job FROM emp WHERE ename='ALLEN')
AND sal>(SELECT sal FROM emp WHERE empno=7521)
#把ALLEN自己去掉
AND ename<>'ALLEN';
✨效果如下:
✨✨单行子查询
子查询返回的是单行多列的数据,就是一条记录
SELECT * FROM emp WHERE (job,sal)=(SELECT job,sal FROM emp WHERE ename='SCOTT')
AND ename<>'SCOTT';
SELECT * FROM emp WHERE (job,mgr)=(SELECT job,mgr FROM emp WHERE empno=7566)
AND emp<>7566;
✨✨列子查询(多行子查询)
子查询返回的是多行单列的数据,就是一列数据。多行子查询也称为集合比较子查询,
在使用多行子查询需要使用多行比较操作符:
操作符 | 含义 |
---|---|
IN | 等于列表中的任意一个 |
ANY | 需要和单行比较操作符一起使用(>、<、=、<>…),与子查询结果中任何一个值比较,一个成立 |
ALL | 需要和单行比较操作符一起使用(>、<、=、<>…),和子查询返回的所有值比较,同时成立 |
SOME | 实际上是ANY的别名,作用相同,一般用ANY |
IN操作符
IN 运算符用来判断表达式的值是否位于给出的列表中;如果是,返回值为 1,否则返回值为 0。
NOT IN 的作用和 IN 恰好相反,NOT IN 用来判断表达式的值是否不存在于给出的列表中;如果不是,返回值为 1,否则返回值为 0。
SELECT * FROM emp WHERE sal IN(SELECT MIN(sal) FROM emp GROUP BY deptno)
AND deptno IS NOT NULL;
ANY操作符
ANY关键字是一个MySQL运算符,如果子查询条件中ANY的比较结果为TRUE,则它会返回布尔值TRUE 。
SELECT * FROM emp WHERE sal >ANY(
SELECT MIN(sal) FROM emp WHERE job='MANAGER' GROUP BY deptno);
ALL操作符
ALL关键字是一个MySQL运算符,如果子查询条件中ALL的比较结果为TRUE,则它会返回布尔值TRUE 。
✨效果如下:
✨✨表子查询
子查询返回的是多行多列的数据,就是一个表格
必须使用 IN、ANY 和 ALL 操作符对子查询返回的结果进行比较
✨综合练习:
DATE_FORMAT(hiredate,'%Y')
将入职日期转换为年份SELECT * FROM emp
WHERE (DATE_FORMAT(hiredate,'%Y'),mgr) IN (SELECT DATE_FORMAT(hiredate,'%Y') hiryear,mgr FROM emp WHERE deptno=10);
SELECT d.deptno,d.dname,d.loc,COUNT(e.deptno),ROUND(AVG(sal),2) FROM
dept d LEFT JOIN emp e
ON e.deptno=d.deptno
GROUP BY d.deptno,d.dname,d.loc;
SELECT dept.deptno,dept.dname,dept.loc,d.count,d.avgsal FROM dept LEFT JOIN
(SELECT deptno,COUNT(*) count,AVG(sal) avgsal FROM emp GROUP BY deptno) d
ON dept.deptno=d.deptno;
#1
SELECT e.empno,e.ename,e.sal,e.comm,e.job,e.hiredate,minsal,maxsal,e.deptno
FROM emp e JOIN
(SELECT deptno,MIN(sal) minsal,MAX(sal) maxsal FROM emp GROUP BY deptno) td
ON e.deptno=td.deptno AND e.deptno=(SELECT deptno FROM dept WHERE dname='SALES');
#2
SELECT e.empno,e.ename,e.sal,e.comm,e.job,e.hiredate,minsal,maxsal,e.deptno
FROM emp e JOIN
(SELECT deptno,MIN(sal) minsal,MAX(sal) maxsal FROM emp GROUP BY deptno
HAVING deptno=(SELECT deptno FROM dept WHERE dname='SALES')) td
ON e.deptno=td.deptno;
#隐式方式
SELECT e.empno,e.ename,e.sal,d.dname,me.ename 领导,temp.count FROM emp e,dept d,emp me,
(SELECT deptno,COUNT(deptno) count FROM emp e GROUP BY deptno) temp
WHERE e.deptno=d.deptno AND e.mgr=me.empno AND temp.deptno=e.deptno
AND e.sal >ANY(SELECT sal FROM emp WHERE ename IN('ALLEN','CLARK'))
AND e.ename NOT IN('ALLEN','CLARK');
#显示方式
SELECT e.empno,e.ename,e.sal,d.dname,me.ename 领导,temp.count
FROM emp e
JOIN dept d ON e.deptno=d.deptno
LEFT JOIN emp me ON e.mgr=me.empno
JOIN (SELECT deptno,COUNT(deptno) count FROM emp e GROUP BY deptno) temp ON temp.deptno=e.deptno
AND e.sal >ANY(SELECT sal FROM emp WHERE ename IN('ALLEN','CLARK'))
AND e.ename NOT IN('ALLEN','CLARK');
#隐式方式
SELECT e.ename,e.sal,d.dname,temp.count,temp.avgsal
FROM emp e,dept d,(SELECT deptno, COUNT(deptno) count,AVG(sal) avgsal FROM emp GROUP BY deptno) temp
WHERE job='MANAGER' AND e.deptno=d.deptno AND temp.deptno=e.deptno;
#显示方式
SELECT e.ename,e.sal,d.dname,temp.count,temp.avgsal
FROM emp e
JOIN dept d ON e.deptno=d.deptno
JOIN (SELECT deptno, COUNT(deptno) count,AVG(sal) avgsal FROM emp GROUP BY deptno) temp ON temp.deptno=e.deptno
AND job='MANAGER';
#隐式方式
SELECT e.empno,e.ename,e.sal,e.job,e.hiredate,d.dname,d.loc,me.ename 领导,s.grade,temp.count,temp.avgsal,temp.avgyear
FROM emp e,dept d,emp me,salgrade s,(SELECT deptno,COUNT(deptno) count,AVG(sal) avgsal,AVG(TIMESTAMPDIFF(MONTH,hiredate,CURDATE())/12) avgyear FROM emp GROUP BY deptno) temp
WHERE e.deptno=d.deptno AND e.sal>(SELECT AVG(sal) FROM emp)
AND e.mgr=me.empno
AND e.sal BETWEEN s.losal AND s.hisal
AND temp.deptno=e.deptno;
#显示方式
SELECT e.empno,e.ename,e.sal,e.job,e.hiredate,d.dname,d.loc,me.ename 领导,s.grade,temp.count,temp.avgsal,temp.avgyear
FROM emp e
JOIN dept d ON e.deptno=d.deptno AND e.sal>(SELECT AVG(sal) FROM emp)
LEFT JOIN emp me ON e.mgr=me.empno
JOIN salgrade s ON e.sal BETWEEN s.losal AND s.hisal
JOIN (SELECT deptno,COUNT(deptno) count,AVG(sal) avgsal,AVG(TIMESTAMPDIFF(MONTH,hiredate,CURDATE())/12) avgyear FROM emp GROUP BY deptno) temp ON temp.deptno=e.deptno;
- ✨✨ 子查询允许结构化的查询,这样就可以把一个查询语句的每个部分隔开。
- ✨✨子查询提供了另一种方法来执行有些需要复杂的join和union来实现的操作。
- ✨✨在许多人看来,子查询可读性较高。 而实际上,这也是子查询的由来。