当前位置: 代码网 > it编程>数据库>Mysql > MySQL DQL子查询的完整实战

MySQL DQL子查询的完整实战

2026年09月01日 Mysql 我要评论
1. 什么是子查询子查询(subquery)是指出现在其他 sql 语句(如 select、insert、update、delete)中的 select 语句,也被称为内查询(inner query)

1. 什么是子查询

子查询(subquery)是指出现在其他 sql 语句(如 select、insert、update、delete)中的 select 语句,也被称为内查询(inner query)或嵌套查询。外层的主查询(outer query)会使用子查询返回的结果作为条件、数据源或字段值,从而完成更复杂的查询需求。

子查询本质上是一张临时生成的虚表,主查询在执行时会把子查询的结果作为输入。理解子查询的分类和适用场景,是掌握 sql 高级查询能力的关键一步。

2. 子查询的分类

2.1 按子查询出现的位置分类

  • select 后面:仅支持标量子查询,即子查询结果必须是一行一列。
  • where 或 having 后面:支持标量子查询(单行)、列子查询(多行)、行子查询。
  • exists 后面(相关子查询):支持表子查询,用于判断结果是否存在。
  • from 后面:子查询结果充当临时表,必须取别名。

2.2 按结果集的行数列数分类

  • 标量子查询:结果只有一行一列。
  • 列子查询:结果只有一列多行。
  • 行子查询:结果有一行多列。
  • 表子查询:结果多行多列。

3. where 或 having 后面的子查询

这是子查询最常用的位置,用于在条件判断中引用其他查询的结果。使用时有以下几个重要特点:

  • 子查询必须放在小括号内。
  • 子查询一般放在条件的右侧。
  • 标量子查询一般搭配单行操作符使用:><>=<=!=<><=>
  • 列子查询一般搭配多行操作符使用:inanysomeall
  • 子查询先于主查询执行,主查询的条件会用到子查询的结果(虚表)。

3.1 标量子查询(单行子查询)

标量子查询返回的结果只有一行一列,通常搭配单行比较操作符使用。下面通过几个经典案例来理解。

案例一:查询谁的工资比 lex 高。

第一步,先查询 lex 的工资:

select salary
from employees
where first_name = 'lex';

第二步,查询工资大于该结果的员工信息:

select *
from employees
where salary > (
    select salary
    from employees
    where first_name = 'lex'
);

案例二:返回 job_id 与 141 号员工相同,且 salary 比 143 号员工高的员工名、job_id 和工资。

第一步,查询 141 号员工的 job_id:

select job_id
from employees
where employee_id = 141;  -- 结果为 st_clerk

第二步,查询 143 号员工的工资:

select salary
from employees
where employee_id = 143;

第三步,组合条件查询目标员工信息:

select first_name, job_id, salary
from employees
where job_id = (
    select job_id
    from employees
    where employee_id = 141
)
and salary > (
    select salary
    from employees
    where employee_id = 143
);

案例三:返回公司工资最少的员工的 last_name、job_id、salary。

第一步,查询公司最低工资:

select min(salary)
from employees;

第二步,查询工资等于该最低工资的员工信息:

select last_name, job_id, salary
from employees
where salary = (
    select min(salary)
    from employees
);

案例四:查询最低工资大于 50 号部门最低工资的部门 id 和其最低工资。

第一步,查询 50 号部门的最低工资:

select min(salary)
from employees
where department_id = 50;

第二步,查询每个部门的最低工资:

select min(salary), department_id
from employees
group by department_id;

第三步,在第二步基础上增加 having 条件,筛选出最低工资大于 50 号部门最低工资的部门:

select min(salary), department_id
from employees
group by department_id
having min(salary) > (
    select min(salary)
    from employees
    where department_id = 50
);

注意:非法使用标量子查询。标量子查询的结果必须是一行一列,如果子查询返回多行,就会导致非法使用。例如下面的写法是错误的:

-- 错误示例:子查询返回多行,无法与单值比较
select min(salary), department_id
from employees
group by department_id
having min(salary) > (
    select salary
    from employees
    where department_id = 50
);

因为 department_id = 50 可能对应多个员工的工资,子查询返回多行,而 min(salary) 是单值,无法与多行结果直接比较,因此会报错。

3.2 列子查询(多行子查询)

列子查询返回的结果是一列多行,通常搭配多行比较操作符使用:

  • in / not in:等于列表中的任意一个。
  • any / some:和子查询返回的某一个值进行比较。
  • all:和子查询返回的所有值进行比较。

案例一:返回 location_id 是 1400 或 1700 的部门中的所有员工名。

第一步,查询 location_id 为 1400 或 1700 的部门编号:

select distinct department_id
from departments
where location_id in (1400, 1700);

第二步,查询部门号属于上述结果集的员工名:

select first_name
from employees
where department_id in (
    select distinct department_id
    from departments
    where location_id in (1400, 1700)
);

案例二:返回其他工种中,比 job_id 为 it_prog 工种任一工资低的员工的工号、名、job_id 以及 salary。

第一步,查询 job_id 为 it_prog 的所有工资:

select distinct salary
from employees
where job_id = 'it_prog';

第二步,使用 any 操作符,查询工资小于上述任意一个工资的员工信息:

select employee_id, first_name, job_id, salary
from employees
where salary < any (
    select distinct salary
    from employees
    where job_id = 'it_prog'
);

等价写法:小于 it_prog 工种的最大工资。

select employee_id, first_name, job_id, salary
from employees
where salary < (
    select max(salary)
    from employees
    where job_id = 'it_prog'
);

案例三:返回其他工种中,比 job_id 为 it_prog 工种所有工资都低的员工的工号、名、job_id 以及 salary。

第一步,查询 job_id 为 it_prog 的所有工资:

select distinct salary
from employees
where job_id = 'it_prog';

第二步,使用 all 操作符,查询工资小于上述所有工资的员工信息:

select employee_id, first_name, job_id, salary
from employees
where salary < all (
    select distinct salary
    from employees
    where job_id = 'it_prog'
);

等价写法:小于 it_prog 工种的最小工资。

select employee_id, first_name, job_id, salary
from employees
where salary < (
    select min(salary)
    from employees
    where job_id = 'it_prog'
);

3.3 行子查询(结果一行多列)

行子查询返回的结果是一行多列,通常用于同时匹配多个字段的条件场景。

案例:查询员工编号最小并且工资最高的员工信息。

第一步,查询最小的员工编号:

select min(employee_id)
from employees;

第二步,查询最高工资:

select max(salary)
from employees;

第三步,组合条件查询员工信息:

select *
from employees
where employee_id = (
    select min(employee_id)
    from employees
)
and salary = (
    select max(salary)
    from employees
);

4. select 后面的子查询

select 后面仅支持标量子查询,即子查询的结果必须是一行一列,通常用于在查询结果中动态生成一个字段值。

案例一:查询每个部门的员工个数。

select d.*,
    (select count(*)
     from employees e
     where e.department_id = d.department_id) as 个数
from departments d;

这里通过关联子查询,为每个部门动态统计员工数量,结果作为新的一列返回。

案例二:查询员工工号等于 102 的部门名。

select (
    select department_name
    from departments d
    inner join employees e
    on d.department_id = e.department_id
    where e.employee_id = 102
) as 部门名;

5. from 后面的子查询

from 后面的子查询会将查询结果充当一张临时表,此时必须为子查询结果取别名,否则会报错。

案例:查询每个部门的平均工资等级。

第一步,查询每个部门的平均工资:

select round(avg(salary)), department_id
from employees
group by department_id;

第二步,将第一步的结果作为临时表,与 job_grades 表连接,筛选平均工资位于对应工资等级区间内的记录:

select *
from (
    select round(avg(salary)) as ag, department_id
    from employees
    group by department_id
) as ag_dep
inner join job_grades g
on ag_dep.ag between lowest_sal and highest_sal;

通过 from 子查询,可以把复杂的聚合结果当作一张普通表来参与连接查询,极大提升了 sql 的表达能力。

6. exists 后面的子查询(相关子查询)

exists 用于判断子查询是否有结果返回,语法为 exists(完整 sql 语句),返回结果为 1(真)或 0(假)。如果子查询有任意一条记录返回,exists 即为真。

select exists(
    select employee_id
    from employees
    where salary = 80000
);

exists 子查询通常与主查询的字段相关联,因此也被称为相关子查询。它常用于判断某个记录是否存在,从而决定主查询是否保留该行。

7. 子查询使用注意事项总结

  • 子查询必须放在小括号内,保证优先级清晰。
  • 子查询一般放在比较条件的右侧,符合 sql 阅读习惯。
  • 标量子查询搭配单行操作符,列子查询搭配多行操作符。
  • from 后面的子查询必须取别名,否则语法错误。
  • select 后面的子查询仅支持标量子查询,且必须保证只返回一行一列。
  • exists 子查询关注的是结果是否存在,而不是结果的具体内容。
  • 子查询先于主查询执行,主查询依赖子查询的结果(虚表)进行条件过滤。

8. 结语

子查询是 sql 查询中非常强大的工具,掌握标量、列、行、表四种子查询的适用场景,能够帮助开发者写出更简洁、更高效的查询语句。在实际开发中,建议结合 explain 分析执行计划,在子查询和 join 之间做出合理选择,以兼顾可读性与性能。

以上就是mysql dql子查询的完整实战的详细内容,更多关于mysql dql子查询的资料请关注代码网其它相关文章!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2026  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com