MySQL

子查询

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);

分解

  1. 内层:select min(sal) from emp → 一个数字
  2. 外层: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 当「所有并列第一名」。
易错:子查询缩进/括号不成对,语法报错。

小结

  1. 子查询 = 里层先查,外层再用;条件值不确定时才需要。
  2. 最常用在 where / having;把结果当表用在 from(必须别名)。
  3. 「最低 / 最高 / 平均」类需求是子查询经典场景。
  4. 同表改删:注意 ERROR 1093,用嵌套别名绕开。
  5. 能用 order by + limit 简单解决的「第一名」,两种写法都要会。
  6. 下一篇 多表关联:等值连接、内连接、左外/右外连接。

系列导航:总目录 · 上一篇:排序与分页 · 下一篇:多表关联查询

相关阅读

全部文章 →
← 返回列表更多「MySQL」