子查询
MySQL 子查询:where/having/from(及 select)嵌套 SELECT;最低工资、高于平均、同表 update/delete 的 ERROR 1093 与嵌套写法。
本页目录
条件里的「值」如果你自己也算不准(比如「最低工资是多少」),就不能写死数字,而要先查一下,再当条件用——这就是子查询。
系列:MySQL 入门到查询 · 第 13 / 17 篇(第 13 天 · 5 月 3 日)
上一篇:排序与分页
下一篇:多表关联查询
总目录:系列索引
环境说明
- 库: rain(SQL 中写 use train;)
本篇
emp比第 12 天多一列hiredate,请用下面 seed 重建:
use train;
drop table if exists emp;
create table emp(
empno int,
ename varchar(20),
sex varchar(4),
job varchar(20),
sal double,
comm double,
deptno int,
hiredate date
);
insert into emp values
(1001,'马云','男','经理',20000, 5000, 10,'2005-03-01'),
(1002,'王明','男','员工', 8000, null, 20,'2010-06-15'),
(1003,'李梅','女','员工', 7500, 800, 20,'2012-09-01'),
(1004,'张强','男','总监',30000, null, 30,'2003-01-20'),
(1005,'赵静','女','经理',18000, 2000, 30,'2008-11-11'),
(1006,'钱伟','男','员工', 9000, null, 30,'2015-04-04'),
(1007,'孙丽','女','员工', 7200, 300, 30,'2018-08-08'),
(1008,'周杰','男','总监',35000, null, 40,'1998-10-21');
练习数据以本篇 seed 为准。
1. 什么是子查询
子查询:在一条 SQL 里,再写一条(或多条)查询;里层先执行,结果交给外层。
select ... from 表 where 列 = ( select ... );
↑ ↑
外层查询 内层子查询(先跑)
select min(sal) from emp;
-- 先算出一个「最低工资」的值
2. 什么时候需要用 / 不用
| 需求 | 条件里的值明确吗 | 要不要子查询 |
|---|---|---|
| 高于某个已知数值(例如 8000) | 是,写死数字 | 不用 |
| 最低工资的人 | 否,先算 min | 要 |
| 高于公司平均工资 | 否,先算 avg(本篇 seed 上结果约为 16837.5,请以 select avg(sal) 为准) | 要 |
-- 不需要
select * from emp where sal = 8000;
-- 需要
select * from emp where sal = (select min(sal) from emp);
口诀:条件值「不确定、要现算」→ 子查询。
3. 最常见:子查询写在 WHERE
3.1 最低工资的雇员
select min(sal) from emp;
select * from emp where sal = (select min(sal) from emp);
分解
- 内层:
select min(sal) from emp→ 一个数字 - 外层:
where sal = 那个数字
3.2 高于公司平均工资
select avg(sal) from emp;
select * from emp where sal > (select avg(sal) from emp);
3.3 最早入职
select * from emp where hiredate = (select min(hiredate) from emp);
也可以用排序(并列时只出一行):
select * from emp order by hiredate limit 1;
| 方式 | 特点 |
|---|---|
子查询 = min | 同一天多人会都列出 |
order by ... limit 1 | 只显示一行,可能不完整 |
3.4 两个子查询同时用
select * from emp
where hiredate = (select min(hiredate) from emp)
and sal = (select max(sal) from emp);
(若没有人同时满足两个条件,结果为空——属正常。)
4. 写在 HAVING 后面(分组后再比)
select deptno, avg(sal) from emp group by deptno;
select avg(sal) from emp;
select deptno, avg(sal)
from emp
group by deptno
having avg(sal) > (select avg(sal) from emp);
读法:先算出全公司平均工资,再保留「部门平均工资 > 该公司平均」的部门。
聚合比较 →
having;行条件比较 →where(见第 11 天)。
5. 写在 FROM 后面:把结果当「表」
select avg(sal) as a_sal from emp group by job;
select min(a_sal) as min_job_avg
from (select avg(sal) as a_sal from emp group by job) as e;
| 要点 | 说明 |
|---|---|
| 内层查询 | 得到「每个职位的平均工资」多行结果 |
| 当表用 | from ( ... ) as e |
| 必须别名 | 内层聚合列如 as a_sal;派生表要 as e |
业务例:平均工资最低的那个职位(人数 + 平均工资)
select job, count(*), avg(sal)
from emp
group by job
having avg(sal) = (
select min(a_sal)
from (select avg(sal) as a_sal from emp group by job) as e
);
更简单(只取一组):
select job, count(*), avg(sal) as a_sal
from emp
group by job
order by a_sal
limit 1;
易错:
limit 1在平均工资并列时只出一行;要全部并列职位,需用子查询=那个最小平均值。
6. 写在 SELECT 后面(了解即可)
select deptno,
(select max(sal) from emp) as company_max
from emp
where deptno = 10;
每一行都会带一个相同的「全公司最高工资」。入门阶段少用;where/from 更常见。
7. 语法位置速览
| 位置 | 作用 | 使用频率(课堂) |
|---|---|---|
where / having | 子查询结果当条件值 | 最多 |
from | 子查询结果当表(要别名) | 常见 |
select | 子查询结果当一列 | 较少 |
8. UPDATE / DELETE 同一张表(易考、易错)
8.1 值明确:直接改
update emp set sal = 9000 where sal = 8000;
8.2 改「最低工资」的人 → 子查询
-- 先看最低工资
select min(sal) from emp;
-- 直接写在 MySQL 8 会报错(不允许同表既改又查)
update emp set sal = 4800 where sal = (select min(sal) from emp);
常见报错:
ERROR 1093 (HY000): You can't specify target table 'emp' for update in FROM clause
原因:不能对同一张表一边 update/delete 一边在子查询里 select(避免一边改一边算)。
解决:再包一层,让 MySQL 认为是「另一张表」
update emp
set sal = 4800
where sal = (
select m_sal
from (select min(sal) as m_sal from emp) as e
);
select * from emp order by sal limit 3;
8.3 删除最低工资的行
-- 可能同样 1093
delete from emp where sal = (select min(sal) from emp);
-- 嵌套写法
delete from emp
where sal = (
select m_sal
from (select min(sal) as m_sal from emp) as e
);
生产提醒:
update/delete前先select确认行数;练习库可随时重 seed。
9. 性能与替代方案
- 每层子查询都会在内存里形成结果,嵌套越多越慢。
- 「第一名」若可用排序解决,往往更直观:
select * from emp order by sal limit 1;
select * from emp order by hiredate limit 1;
- 子查询在值不确定、或要并列全部时更合适;能
order by + limit且接受只取一行时,两种都可以,先保证业务正确。
10. 完整跟练脚本
use train;
drop table if exists emp;
create table emp(
empno int, ename varchar(20), sex varchar(4), job varchar(20),
sal double, comm double, deptno int, hiredate date
);
insert into emp values
(1001,'马云','男','经理',20000, 5000, 10,'2005-03-01'),
(1002,'王明','男','员工', 8000, null, 20,'2010-06-15'),
(1003,'李梅','女','员工', 7500, 800, 20,'2012-09-01'),
(1004,'张强','男','总监',30000, null, 30,'2003-01-20'),
(1005,'赵静','女','经理',18000, 2000, 30,'2008-11-11'),
(1006,'钱伟','男','员工', 9000, null, 30,'2015-04-04'),
(1007,'孙丽','女','员工', 7200, 300, 30,'2018-08-08'),
(1008,'周杰','男','总监',35000, null, 40,'1998-10-21');
select min(sal) from emp;
select * from emp where sal = (select min(sal) from emp);
select * from emp where sal > (select avg(sal) from emp);
select * from emp where hiredate = (select min(hiredate) from emp);
select deptno, avg(sal) from emp group by deptno
having avg(sal) > (select avg(sal) from emp);
select min(a_sal)
from (select avg(sal) as a_sal from emp group by job) as e;
select job, count(*), avg(sal) as a_sal
from emp group by job order by a_sal limit 1;
select min(sal) from emp;
update emp
set sal = 4800
where sal = (select m_sal from (select min(sal) as m_sal from emp) as e);
select * from emp order by sal limit 2;
11. 速查表
| 目标 | 写法 |
|---|---|
| 最低工资的人 | where sal=(select min(sal) from emp) |
| 高于公司平均 | where sal>(select avg(sal) from emp) |
| 最早入职 | where hiredate=(select min(hiredate) from emp) |
| 部门均薪 > 全公司 | group by deptno having avg(sal)>(select avg(sal) from emp) |
| 派生表 | from (select ... as x from ...) as t |
| 第一名(一行) | order by ... limit 1 |
| 同表 update 子查询 | 再包一层 (select ... as m from emp) as e |
12. 今天的练习清单
- 分步查最低工资 → 再写子查询
- 高于公司平均工资的雇员
- 最早入职(子查询 vs
order by limit 1对比) -
having avg(sal)>(select avg(sal) from emp) -
from子查询求「职位平均工资的最小值」 - 尝试同表
update报 1093,再改嵌套写法 - 解释:为什么
where sal=(select min(sal)...)比写死数字更稳
常见错误
易错:子查询返回多行,却用
=比较(应确保一行,或改in——进阶)。
易错:from (select ...)忘了给表或列起别名。
易错:聚合条件在where,子查询在having更合适(部门均薪场景)。
易错:同表update/delete+ 子查询触发 1093,未嵌套别名。
易错:用limit 1当「所有并列第一名」。
易错:子查询缩进/括号不成对,语法报错。
小结
- 子查询 = 里层先查,外层再用;条件值不确定时才需要。
- 最常用在
where/having;把结果当表用在from(必须别名)。 - 「最低 / 最高 / 平均」类需求是子查询经典场景。
- 同表改删:注意 ERROR 1093,用嵌套别名绕开。
- 能用
order by + limit简单解决的「第一名」,两种写法都要会。 - 下一篇 多表关联:等值连接、内连接、左外/右外连接。