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

1次阅读

子查询在WHERE中需加括号且必须返回单值;IN适用于多值匹配,NOT IN遇NULL失效,应改用NOT EXISTS;FROM中子查询须命名;select中嵌套子查询为相关子查询,性能差,建议用JOIN或CTE替代。

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

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

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

  • 如果想匹配多个值,改用 INWHERE 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 EXISTSNOT IN 的推荐替代方案,避免因子查询含 NULL 导致整条语句无结果
  • 子查询中用 SELECT 1 是惯例,语义清晰、无实际数据传输开销

子查询写起来顺手,但真正上线前得盯紧 EXPLaiN 输出里有没有 DEPENDENT SUBQUERY —— 这意味着它正在逐行重执行,而你可能根本没意识到。

text=ZqhQzanResources