一、子查询定义
当我们进⾏语句查询的时候,总会遇到我们需要的条件需要通过另⼀个查询语句查询出来后才能进⾏,就是说A 查询语句需要依赖B 查询语句的查询结果,B 查询就是⼦查询,A 查询语句就是主查询,⼀个SQL语句可以包含多个⼦查询。
二、子查询出现的地方
- 在where⼦句中: ⼦查询的结果(具体)可⽤作条件筛选时使⽤的值。
- 在from⼦句中: ⼦查询的结果可充当⼀张表或视图,需要使⽤表别名。
- 在having⼦句中: ⼦查询的结果可⽤作分组查询再次条件过滤时使⽤的值
- 在select⼦句中: ⼦查询的结果可充当⼀个字段。仅限⼦查询返回单⾏单列的情况。(这种情况下会输出没有结果的话结果会有null的情况)
1、子查询出现在where子句中
go
1、查询⼯资⼤于10号部⻔的平均⼯资的⾮10号部⻔的员⼯信息
分析:
1)10号部⻔的平均⼯资
select avg(sal) from emp where deptno = 10;
2)查询员工信息
select * from emp where deptno != 10 and sal > (select avg(sal) from emp where deptno = 10);
+-------+-------+---------+------+------------+---------+------+--------+
| empno | ename | job | mgr | hiredate | sal | comm | deptno |
+-------+-------+---------+------+------------+---------+------+--------+
| 7566 | JONES | MANAGER | 7839 | 1981-04-02 | 2975.00 | NULL | 20 |
| 7788 | SCOTT | ANALYST | 7566 | 1987-04-19 | 3000.00 | NULL | 20 |
| 7902 | FORD | ANALYST | 7566 | 1981-12-03 | 3000.00 | NULL | 20 |
+-------+-------+---------+------+------------+---------+------+--------+
2、查询与7369同部⻔的同事信息
select deptno from emp where empno = 7369;
select * from emp where deptno = (select deptno from emp where empno = 7369)
and empno != 7369;
+-------+-------+---------+------+------------+---------+------+--------+
| empno | ename | job | mgr | hiredate | sal | comm | deptno |
+-------+-------+---------+------+------------+---------+------+--------+
| 7566 | JONES | MANAGER | 7839 | 1981-04-02 | 2975.00 | NULL | 20 |
| 7788 | SCOTT | ANALYST | 7566 | 1987-04-19 | 3000.00 | NULL | 20 |
| 7876 | ADAMS | CLERK | 7788 | 1987-05-23 | 1100.00 | NULL | 20 |
| 7902 | FORD | ANALYST | 7566 | 1981-12-03 | 3000.00 | NULL | 20 |
+-------+-------+---------+------+------------+---------+------+--------+
2、子查询出现在from子句中
go
1、查询员⼯的姓名,⼯资,及其部⻔的平均⼯资
select ename,sal from emp ;
思考:
每一个部门的平均工资是多少?
select deptno,avg(sal) from emp group by deptno;
+--------+-------------+
| deptno | avg(sal) |
+--------+-------------+
| 10 | 2916.666667 |
| 20 | 2175.000000 |
| 30 | 1566.666667 |
| 100 | NULL |
+--------+-------------+
4 rows in set (0.09 sec)
查询员工信息:
select * from emp;
mysql> select * from emp;
+-------+--------+-----------+------+------------+---------+---------+--------+
| empno | ename | job | mgr | hiredate | sal | comm | deptno |
+-------+--------+-----------+------+------------+---------+---------+--------+
| 7369 | SMITH | CLERK | 7902 | 1980-12-17 | 800.00 | NULL | 20 |
| 7499 | ALLEN | SALESMAN | 7698 | 1981-02-20 | 1600.00 | 300.00 | 30 |
| 7521 | WARD | SALESMAN | 7698 | 1981-02-22 | 1250.00 | 500.00 | 30 |
| 7566 | JONES | MANAGER | 7839 | 1981-04-02 | 2975.00 | NULL | 20 |
| 7654 | MARTIN | SALESMAN | 7698 | 1981-09-28 | 1250.00 | 1400.00 | 30 |
| 7698 | BLAKE | MANAGER | 7839 | 1981-05-01 | 2850.00 | NULL | 30 |
| 7782 | CLARK | MANAGER | 7839 | 1981-06-09 | 2450.00 | NULL | 10 |
| 7788 | SCOTT | ANALYST | 7566 | 1987-04-19 | 3000.00 | NULL | 20 |
| 7839 | KING | PRESIDENT | NULL | 1981-11-17 | 5000.00 | NULL | 10 |
| 7844 | TURNER | SALESMAN | 7698 | 1981-09-08 | 1500.00 | 0.00 | 30 |
| 7876 | ADAMS | CLERK | 7788 | 1987-05-23 | 1100.00 | NULL | 20 |
| 7900 | JAMES | CLERK | 7698 | 1981-12-03 | 950.00 | NULL | 30 |
| 7902 | FORD | ANALYST | 7566 | 1981-12-03 | 3000.00 | NULL | 20 |
| 7934 | MILLER | CLERK | 7782 | 1982-01-23 | 1300.00 | NULL | 10 |
| 8000 | LaoYan | NULL | NULL | NULL | NULL | NULL | 100 |
+-------+--------+-----------+------+------------+---------+---------+--------+
此时:查询出来的平均工资表可以当做一个虚拟的表,和emp表关联起来
mysql> select * from
( select deptno,avg(sal) avgMoney from emp group by deptno ) avgTable;
+--------+-------------+
| deptno | avgMoney |
+--------+-------------+
| 10 | 2916.666667 |
| 20 | 2175.000000 |
| 30 | 1566.666667 |
| 100 | NULL |
+--------+-------------+
现在 avgTable 表 和 emp 通过 deptno 相关联
select
ename,sal,avgMoney from
emp ,
(select deptno,avg(sal) avgMoney from emp group by deptno) avgTable
where emp.deptno=avgTable.deptno;
+--------+---------+-------------+
| ename | sal | avgMoney |
+--------+---------+-------------+
| SMITH | 800.00 | 2175.000000 |
| ALLEN | 1600.00 | 1566.666667 |
| WARD | 1250.00 | 1566.666667 |
| JONES | 2975.00 | 2175.000000 |
| MARTIN | 1250.00 | 1566.666667 |
| BLAKE | 2850.00 | 1566.666667 |
| CLARK | 2450.00 | 2916.666667 |
| SCOTT | 3000.00 | 2175.000000 |
| KING | 5000.00 | 2916.666667 |
| TURNER | 1500.00 | 1566.666667 |
| ADAMS | 1100.00 | 2175.000000 |
| JAMES | 950.00 | 1566.666667 |
| FORD | 3000.00 | 2175.000000 |
| MILLER | 1300.00 | 2916.666667 |
| LaoYan | NULL | NULL |
+--------+---------+-------------+
最终的答案!
虚拟机的表解释:--任何的查询结果都可以当做一个虚拟机的表来使用!
mysql> select ename,job,sal from emp where job is not null;
+--------+-----------+---------+
| ename | job | sal |
+--------+-----------+---------+
| SMITH | CLERK | 800.00 |
| ALLEN | SALESMAN | 1600.00 |
| WARD | SALESMAN | 1250.00 |
| JONES | MANAGER | 2975.00 |
| MARTIN | SALESMAN | 1250.00 |
| BLAKE | MANAGER | 2850.00 |
| CLARK | MANAGER | 2450.00 |
| SCOTT | ANALYST | 3000.00 |
| KING | PRESIDENT | 5000.00 |
| TURNER | SALESMAN | 1500.00 |
| ADAMS | CLERK | 1100.00 |
| JAMES | CLERK | 950.00 |
| FORD | ANALYST | 3000.00 |
| MILLER | CLERK | 1300.00 |
+--------+-----------+---------+
14 rows in set (0.09 sec)
mysql> select * from (select ename,job,sal from emp where job is not null) eemp;
+--------+-----------+---------+
| ename | job | sal |
+--------+-----------+---------+
| SMITH | CLERK | 800.00 |
| ALLEN | SALESMAN | 1600.00 |
| WARD | SALESMAN | 1250.00 |
| JONES | MANAGER | 2975.00 |
| MARTIN | SALESMAN | 1250.00 |
| BLAKE | MANAGER | 2850.00 |
| CLARK | MANAGER | 2450.00 |
| SCOTT | ANALYST | 3000.00 |
| KING | PRESIDENT | 5000.00 |
| TURNER | SALESMAN | 1500.00 |
| ADAMS | CLERK | 1100.00 |
| JAMES | CLERK | 950.00 |
| FORD | ANALYST | 3000.00 |
| MILLER | CLERK | 1300.00 |
+--------+-----------+---------+
14 rows in set (0.06 sec)
3.子查询在select语句中
go
2、查询员⼯的姓名,⼯资,及其部⻔的平均⼯资
说一个情况: emp表和emp表也是可以自己关联自己的。
select * from emp e1, emp e2 where e1.empno = e2.empno;
select ename,sal, (select avg(sal) from emp e2 where e2.deptno = e1.deptno )
部门平均工资 from emp e1;
拆解:
select avg(sal) from emp e2 where e2.deptno = 10;
select avg(sal) from emp e2 where e2.deptno = 20;
最终的答案:
+--------+---------+--------------+
| ename | sal | 部门平均工资 |
+--------+---------+--------------+
| SMITH | 800.00 | 2175.000000 |
| ALLEN | 1600.00 | 1566.666667 |
| WARD | 1250.00 | 1566.666667 |
| JONES | 2975.00 | 2175.000000 |
| MARTIN | 1250.00 | 1566.666667 |
| BLAKE | 2850.00 | 1566.666667 |
| CLARK | 2450.00 | 2916.666667 |
| SCOTT | 3000.00 | 2175.000000 |
| KING | 5000.00 | 2916.666667 |
| TURNER | 1500.00 | 1566.666667 |
| ADAMS | 1100.00 | 2175.000000 |
| JAMES | 950.00 | 1566.666667 |
| FORD | 3000.00 | 2175.000000 |
| MILLER | 1300.00 | 2916.666667 |
| LaoYan | NULL | NULL |
+--------+---------+--------------+
优化一下:工资 保留小数点后2位
select ename,sal,
round(
( select avg(sal) from emp e2 where e2.deptno = e1.deptno),2) 部门平均工资
from emp e1;
+--------+---------+--------------+
| ename | sal | 部门平均工资 |
+--------+---------+--------------+
| SMITH | 800.00 | 2175.00 |
| ALLEN | 1600.00 | 1566.67 |
| WARD | 1250.00 | 1566.67 |
| JONES | 2975.00 | 2175.00 |
| MARTIN | 1250.00 | 1566.67 |
| BLAKE | 2850.00 | 1566.67 |
| CLARK | 2450.00 | 2916.67 |
| SCOTT | 3000.00 | 2175.00 |
| KING | 5000.00 | 2916.67 |
| TURNER | 1500.00 | 1566.67 |
| ADAMS | 1100.00 | 2175.00 |
| JAMES | 950.00 | 1566.67 |
| FORD | 3000.00 | 2175.00 |
| MILLER | 1300.00 | 2916.67 |
| LaoYan | NULL | NULL |
+--------+---------+--------------+
再来一遍,仔细品味:
mysql> select ename,sal, ( select avg(sal) from emp e2 where e2.deptno = 10 ) from emp;
+--------+---------+------------------------------------------------------+
| ename | sal | ( select avg(sal) from emp e2 where e2.deptno = 10 ) |
+--------+---------+------------------------------------------------------+
| SMITH | 800.00 | 2916.666667 |
| ALLEN | 1600.00 | 2916.666667 |
| WARD | 1250.00 | 2916.666667 |
| JONES | 2975.00 | 2916.666667 |
| MARTIN | 1250.00 | 2916.666667 |
| BLAKE | 2850.00 | 2916.666667 |
| CLARK | 2450.00 | 2916.666667 |
| SCOTT | 3000.00 | 2916.666667 |
| KING | 5000.00 | 2916.666667 |
| TURNER | 1500.00 | 2916.666667 |
| ADAMS | 1100.00 | 2916.666667 |
| JAMES | 950.00 | 2916.666667 |
| FORD | 3000.00 | 2916.666667 |
| MILLER | 1300.00 | 2916.666667 |
| LaoYan | NULL | 2916.666667 |
+--------+---------+------------------------------------------------------+
15 rows in set (0.10 sec)
mysql> select ename,sal, ( select avg(sal) from emp e2 where e2.deptno = emp.deptno ) from emp;
+--------+---------+--------------------------------------------------------------+
| ename | sal | ( select avg(sal) from emp e2 where e2.deptno = emp.deptno ) |
+--------+---------+--------------------------------------------------------------+
| SMITH | 800.00 | 2175.000000 |
| ALLEN | 1600.00 | 1566.666667 |
| WARD | 1250.00 | 1566.666667 |
| JONES | 2975.00 | 2175.000000 |
| MARTIN | 1250.00 | 1566.666667 |
| BLAKE | 2850.00 | 1566.666667 |
| CLARK | 2450.00 | 2916.666667 |
| SCOTT | 3000.00 | 2175.000000 |
| KING | 5000.00 | 2916.666667 |
| TURNER | 1500.00 | 1566.666667 |
| ADAMS | 1100.00 | 2175.000000 |
| JAMES | 950.00 | 1566.666667 |
| FORD | 3000.00 | 2175.000000 |
| MILLER | 1300.00 | 2916.666667 |
| LaoYan | NULL | NULL |
+--------+---------+--------------------------------------------------------------+
15 rows in set (0.11 sec)
再来一题:
查询每个员⼯的信息及其部⻔的平均⼯资,⼯资之和,部⻔⼈数
select *,
(select avg(sal) from emp e1 where e1.deptno = emp.deptno) 平均工资,
(select sum(sal) from emp e2 where e2.deptno = emp.deptno) 工资之和,
(select count(*) from emp e3 where e3.deptno = emp.deptno) 部门人数
from emp;
尝试-- 这种不行,报 Operand should contain 1 column(s)
select *,(
select avg(sal) 平均工资,sum(sal) 工资之和,count(*) 部门人数 from emp e1 where e1.deptno = emp.deptno) from emp;
改变思路:在玩一种很新的东西
mysql> select deptno,avg(sal) 平均工资,sum(sal) 工资之和,count(*) 部门人数 from emp e1
group by deptno ;
+--------+-------------+----------+----------+
| deptno | 平均工资 | 工资之和 | 部门人数 |
+--------+-------------+----------+----------+
| 10 | 2916.666667 | 8750.00 | 3 |
| 20 | 2175.000000 | 10875.00 | 5 |
| 30 | 1566.666667 | 9400.00 | 6 |
| 100 | NULL | NULL | 1 |
+--------+-------------+----------+----------+
硬套进去:失败了,原因是这个地方只允许出现一列数据
select * , (select * from (select deptno,avg(sal) 平均工资,sum(sal) 工资之和,count(*) 部门人数 from emp e1
group by deptno) pgb where pgb.deptno =emp.deptno ) from emp;
4.having后有子查询
go
查询平均⼯资⼤于30号部⻔的平均⼯资的部⻔号,和平均⼯资
分析:30号部⻔的平均⼯资
select avg(sal) from emp where deptno = 30;
查询所有部门的平局工资:
select deptno,avg(sal) from emp group by deptno;
整合
select deptno,avg(sal) avgMoney from emp group by deptno
having avgMoney > (select avg(sal) from emp where deptno = 30);
+--------+-------------+
| deptno | avgMoney |
+--------+-------------+
| 10 | 2916.666667 |
| 20 | 2175.000000 |
+--------+-------------+
三.关键字EXISTS,ANY,ALL的子查询
举个例子:
1.EXISTS关键字:
EXISTS关键字后面的参数可以是任意一个子查询,它不产生任何数据只返回TRUE或FALSE。
当返回值为TRUE时外层查询才会执行。
假如王五同学在学生表中则从班级表查询所有班级信息 MySQL命令
bash
select * from class where exists (select * from student where sname='王五');
2.带ANY关键字的子查询
ANY关键字表示满足其中任意一个条件就返回一个结果作为外层查询条件。
查询比任一学生所属班级号还大的班级编号 MySQL命令:
bash
select * from class where cid > any (select classid from student);
3.带ALL关键字的子查询
ALL关键字与ANY有点类似,只不过带ALL关键字的子査询返回的结果需同时满足所有内层査询条件。
bash
select * from class where cid > all (select classid from student);
多表查询
1、多表查询语法
bash
select 字段1,字段2... from 表1,表2... [where 条件]
查询学生表和成绩表所有信息
bash
select * from student,sc where student.sid = sc.sid
注意: 多表查询时,一定要找到两个表中相互关联的字段,并且作为条件使用
2.多表链接查询
bash
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for course
-- ----------------------------
DROP TABLE IF EXISTS `course`;
CREATE TABLE `course` (
`cid` int(11) NOT NULL COMMENT '课程号',
`cname` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '课程名',
`tid` int(11) NULL DEFAULT NULL COMMENT '老师号',
PRIMARY KEY (`cid`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of course
-- ----------------------------
INSERT INTO `course` VALUES (3001, '英语', 1);
INSERT INTO `course` VALUES (3002, '数学', 2);
INSERT INTO `course` VALUES (3003, '物理', 3);
INSERT INTO `course` VALUES (3004, '语文', 4);
-- ----------------------------
-- Table structure for sc
-- ----------------------------
DROP TABLE IF EXISTS `sc`;
CREATE TABLE `sc` (
`sid` int(11) NOT NULL COMMENT '学生号',
`cid` int(11) NOT NULL COMMENT '课程号',
`score` int(11) NULL DEFAULT NULL COMMENT '成绩'
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of sc
-- ----------------------------
INSERT INTO `sc` VALUES (101, 3001, 90);
INSERT INTO `sc` VALUES (102, 3001, 85);
INSERT INTO `sc` VALUES (103, 3001, 76);
INSERT INTO `sc` VALUES (104, 3002, 87);
INSERT INTO `sc` VALUES (105, 3001, 92);
INSERT INTO `sc` VALUES (101, 3002, 81);
INSERT INTO `sc` VALUES (102, 3002, 93);
INSERT INTO `sc` VALUES (103, 3002, 73);
INSERT INTO `sc` VALUES (104, 3002, 65);
INSERT INTO `sc` VALUES (105, 3002, 96);
INSERT INTO `sc` VALUES (101, 3003, 85);
INSERT INTO `sc` VALUES (102, 3003, 76);
INSERT INTO `sc` VALUES (103, 3003, 63);
INSERT INTO `sc` VALUES (104, 3003, 59);
INSERT INTO `sc` VALUES (105, 3003, 56);
INSERT INTO `sc` VALUES (101, 3004, 100);
INSERT INTO `sc` VALUES (102, 3004, 83);
INSERT INTO `sc` VALUES (103, 3004, 75);
INSERT INTO `sc` VALUES (104, 3004, 69);
INSERT INTO `sc` VALUES (105, 3004, 50);
INSERT INTO `sc` VALUES (106, 3001, 60);
INSERT INTO `sc` VALUES (106, 3001, 60);
-- ----------------------------
-- Table structure for student
-- ----------------------------
DROP TABLE IF EXISTS `student`;
CREATE TABLE `student` (
`sid` int(11) NOT NULL COMMENT '学生号',
`sname` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '姓名',
`sage` int(11) NULL DEFAULT NULL COMMENT '年龄',
`ssex` varchar(8) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '性别',
PRIMARY KEY (`sid`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of student
-- ----------------------------
INSERT INTO `student` VALUES (101, '王明', 18, '女');
INSERT INTO `student` VALUES (102, '王天', 19, '男');
INSERT INTO `student` VALUES (103, '张三', 18, '男');
INSERT INTO `student` VALUES (104, '李四', 19, '女');
INSERT INTO `student` VALUES (105, '王五', 20, '男');
INSERT INTO `student` VALUES (107, '万源', 17, '男');
-- ----------------------------
-- Table structure for teacher
-- ----------------------------
DROP TABLE IF EXISTS `teacher`;
CREATE TABLE `teacher` (
`tid` int(11) NOT NULL COMMENT '老师号',
`tname` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '老师名称',
PRIMARY KEY (`tid`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of teacher
-- ----------------------------
INSERT INTO `teacher` VALUES (1, '王老师');
INSERT INTO `teacher` VALUES (2, '李老师');
INSERT INTO `teacher` VALUES (3, '赵老师');
INSERT INTO `teacher` VALUES (4, '宋老师');
SET FOREIGN_KEY_CHECKS = 1;
多表连接查询语法(重点)
bash
SELECT 字段列表
FROM 表1 INNER|LEFT|RIGHT JOIN 表2
ON 表1.字段 = 表2.字段;
①内连接查询 (只显示符合条件的数据)
查询学生表和成绩表所有信息
bash
select * from student inner join sc on student.sid = sc.sid;
内连接查询与多表联合查询的效果是一样的.
②左外连接查询 (左边表中的数据优先全部显示)
查询学生表和成绩表所有信息
bash
select * from student left join sc on student.sid = sc.sid;
学生表中的数据全部都显示,而成绩表中的数据符合条件的才会显示,不符合条件的会以 null 进行填充
③右外连接查询 (右边表中的数据优先全部显示)
查询学生表和成绩表所有信息
bash
select * from student right join sc on student.sid = sc.sid;
正好与[左外连接相反]
3、三表查询
①查询所有人的英语成绩
语法:
bash
select 表1.字段,表2.字段,表3.字段 from 表1 join 表2 on 表1.关联字段 = 表2.关联字段 join 表3 on 表2.关联字段 = 表3.关联字段 where....;
bash
select student.sname,course.cname,sc.score from student join sc on student.sid = sc.sid join course on sc.cid = course.cid where course.cname = "英语";
最后很强了