易君召
发布于 2026-08-15 / 作者:易君召 / 3 阅读
0

SQL 子查询入门学习

子查询:把一条 SELECT 查询,嵌套在另一条 SQL 里面(括号包起来),内层查询叫子查询,外层叫父查询。

子查询可以写在 WHEREFROMSELECTEXISTS 后面,返回单行、多行、一张表。

核心口诀:括号包起来,先跑内层,结果交给外层用

1. 分类(按返回结果)

① 标量子查询(返回单个值:1 行 1 列)

内层只返回一个数值,可直接用 =>< 比较。

sql

-- 查询工资高于平均工资的员工
SELECT name, salary 
FROM emp 
WHERE salary > (SELECT AVG(salary) FROM emp);

执行顺序:

  1. 先执行括号内:算出平均工资(比如 8000)

  2. 外层变成:WHERE salary > 8000

⚠️注意:子查询返回多行时,不能用 =,会报错。

② 列子查询(返回一列多行

返回一堆值,要用 IN / ANY / ALL

sql

-- 查询在销售部的所有员工
SELECT name FROM emp 
WHERE dept_id IN (SELECT id FROM dept WHERE dept_name='销售部');
  • IN:等于子查询结果里任意一个

  • > ANY:大于子查询里任意一个(大于最小值)

  • > ALL:大于子查询全部(大于最大值)

③ 表子查询(返回完整一张表,多行多列)

写在 FROM 后面,又叫派生表 / 内联视图必须给子查询起别名

sql

-- 先统计每个部门平均工资,再对这个结果过滤
SELECT * 
FROM (
    SELECT dept_id, AVG(salary) avg_sal 
    FROM emp 
    GROUP BY dept_id
) t  -- 必须写别名t
WHERE avg_sal > 9000;

关键点:FROM 后面的子查询,一定要别名,否则语法报错。

④ EXISTS 相关子查询

普通子查询:内层独立执行,不依赖外层。

相关子查询:子查询引用外层表的字段,内层每一行都要跑一次。

EXISTS:判断子查询有没有返回行,有就 true,没有 false。

sql

-- 查询存在员工的部门
SELECT d.dept_name
FROM dept d
WHERE EXISTS (
    SELECT 1 FROM emp e WHERE e.dept_id = d.id
);

逻辑:遍历外层 dept 每一行,传给内层,看这个部门下有没有员工。

推荐写 SELECT 1,不需要查真实字段,只判断存在,性能更好。

2. 子查询可以放的 4 个位置

  1. WHERE 后面:过滤条件(最常用)

  2. FROM 后面:派生表,当做一张临时表使用

  3. SELECT 后面:标量子查询,作为字段输出(性能差,少用)

sql

SELECT 
    name,
    (SELECT dept_name FROM dept WHERE id=emp.dept_id) dept_name
FROM emp;
  1. HAVING 后面:分组后的条件过滤

3. 子查询 vs JOIN(重点面试考点)

很多子查询逻辑,都可以改写为 JOIN。

示例:子查询 IN 写法

sql

SELECT name FROM emp WHERE dept_id IN (SELECT id FROM dept where dept_name='销售部');

等价 JOIN 写法

sql

SELECT e.name 
FROM emp e
JOIN dept d ON e.dept_id=d.id
WHERE d.dept_name='销售部';

怎么选?

  1. IN + 子查询:数据量大容易性能差;MySQL5.5 以前会有子查询优化坑。

  2. EXISTS:适合外层表小、内层表大,找到匹配就停止,效率高。

  3. JOIN:数据库优化器可以选择索引,绝大多数场景优先 JOIN。

  4. 相关子查询:外层每一行执行一次子查询,大表要谨慎,容易慢。

4. 常见坑

  1. FROM 后的子查询忘记写别名 →语法报错

  2. 返回多行的子查询用 =,而不是 IN →报错 subquery returns more than 1 row

  3. SELECT 后面写相关子查询,千万级大表性能爆炸

  4. NOT IN 子查询结果包含 NULL,查询结果直接为空!

NOT IN(1,2,NULL) 结果永远 false,尽量改用 NOT EXISTS

sql

-- 不推荐,子查询有null会出问题
SELECT * FROM emp WHERE id NOT IN (SELECT emp_id FROM other);

-- 推荐写法
SELECT * FROM emp e WHERE NOT EXISTS (SELECT 1 FROM other o WHERE o.emp_id = e.id);

5. 快速区分:相关子查询 / 非相关子查询

  • 非相关子查询:子查询完全独立,可以单独拿出来执行一遍。WHERE salary > (select avg(salary) from emp)

  • 相关子查询:子查询里面用到外层表的列,不能单独运行。EXISTS 例子就是典型。

简单总结

  1. 子查询就是括号包裹的 select,先执行内层;

  2. 返回单个值用=;返回多值用IN/ANY/ALL

  3. FROM 后子查询必须别名;

  4. EXISTS 适合判断存在,NOT IN 小心 NULL 坑;

  5. 能 JOIN 优先 JOIN,大表慎用相关子查询。