mysql如何使用子查询_mysql nested select语法说明

来源:这里教程网 时间:2026-02-28 20:46:09 作者:

WHERE 里用子查询做动态条件,但别忘了括号和单值约束

子查询最常用的地方就是

WHERE
子句,比如“查工资高于平均值的员工”:
SELECT * FROM employees WHERE salary > (SELECT AVG(salary) FROM employees)
。这里子查询必须加括号,且返回结果只能是**一行一列**(标量),否则 MySQL 会直接报错
Subquery returns more than 1 row

如果想匹配多个值,改用
IN
WHERE dept_id IN (SELECT id FROM departments WHERE active = 1)
遇到
NOT IN
时要格外小心:只要子查询结果里有任意一个
NULL
,整个条件就恒为
UNKNOWN
,查不出任何数据;建议换成
NOT EXISTS
非相关子查询(不依赖外层字段)只执行一次,性能好;相关子查询(如
WHERE salary > (SELECT AVG(salary) FROM employees e2 WHERE e2.dept_id = e1.dept_id)
)每行都重算,大数据量时容易变慢

FROM 中写子查询当临时表,别名不是可选而是强制

把子查询放在

FROM
子句中,本质是生成一个派生表(derived table),它像一张临时视图一样供主查询使用。但 MySQL 要求你必须给它起别名,否则语法错误:
SELECT * FROM (SELECT user_id, COUNT(*) c FROM orders GROUP BY user_id) AS t
—— 注意末尾的
AS t
不可省略。

派生表里不能直接写
ORDER BY
,除非配合
LIMIT
(如
ORDER BY created_at DESC LIMIT 10
适合做中间聚合或过滤,比如先筛出“近30天下单用户”,再统计他们的人均消费 MySQL 5.7 及以前不支持在派生表中引用外部查询字段(即不能做相关派生表),8.0+ 仍受限,不如 CTE 直观

SELECT 列表里嵌套子查询,只允许返回单个值

SELECT
后面加子查询,常用于给每行补一个计算字段,比如“每个用户的订单数”:
SELECT id, name, (SELECT COUNT(*) FROM orders WHERE user_id = users.id) AS order_count FROM users
。这个子查询必须对每一行都只返回一个值,否则报错。

这种写法看似简洁,但实际是**相关子查询**,N 行用户就要执行 N 次子查询,性能隐患明显 等价但更高效的做法是用
LEFT JOIN + GROUP BY
或 MySQL 8.0+ 的 CTE:
WITH user_orders AS (SELECT user_id, COUNT(*) c FROM orders GROUP BY user_id) SELECT u.*, COALESCE(o.c, 0) FROM users u LEFT JOIN user_orders o ON u.id = o.user_id
别在同一个
SELECT
列表里重复写相同子查询(比如两次查部门平均薪资),MySQL 不会自动缓存,会真执行两次

EXISTS 比 IN 更安全,尤其当子查询可能含 NULL 时

判断“是否存在关联记录”时,

EXISTS
是比
IN
更可靠的选择。例如查有订单的客户:
SELECT name FROM customers c WHERE EXISTS (SELECT 1 FROM orders o WHERE o.customer_id = c.id)
。它不关心子查询返回什么值,只判断有没有结果集,且天然规避
NULL
导致逻辑失效的问题。

EXISTS
通常比
IN
更快,因为找到第一条匹配就终止,而
IN
往往需穷举全部结果
NOT EXISTS
NOT IN
的推荐替代方案,避免因子查询含
NULL
导致整条语句无结果
子查询中用
SELECT 1
是惯例,语义清晰、无实际数据传输开销

子查询写起来顺手,但真正上线前得盯紧

EXPLAIN
输出里有没有
DEPENDENT SUBQUERY
—— 这意味着它正在逐行重执行,而你可能根本没意识到。

相关推荐