mysql如何使用subquery子查询_mysql嵌套查询用法

4次阅读

子查询在where中用于单值比较需确保一行一列,多行用in或exists;from中作派生表须加别名;update/delete中不可直接引用目标表,需嵌套派生表或改用join。

mysql如何使用subquery子查询_mysql嵌套查询用法

WHERE 子句里用 select 返回单值子查询

这是最常见也最安全的用法:子查询必须返回且仅返回一行一列,否则会报错 Subquery returns more than 1 row。适用于条件过滤,比如查“工资高于平均工资的员工”。

  • WHERE salary > (SELECT AVG(salary) FROM employees) —— 正确,聚合函数保证单值
  • WHERE dept_id = (SELECT dept_id FROM departments WHERE name = 'Tech') —— 要求 name 唯一,否则可能报错
  • 如果不确定唯一性,改用 IN 或加 LIMIT 1(但后者逻辑可能出错,慎用)
  • mysql 8.0+ 支持标量子查询自动转为 NULL(当无结果时),但老版本会报错 Subquery returns no rows

INEXISTS 处理多行结果子查询

当子查询要返回多行(比如一批 ID 列表),= 就不适用了,得换操作符。二者语义不同,性能差异明显。

  • WHERE user_id IN (SELECT id FROM banned_users) —— IN 适合小结果集;若子查询结果含 NULL,整条条件会判为 UNKNOWN,该行被过滤掉
  • WHERE EXISTS (SELECT 1 FROM orders o WHERE o.user_id = u.id AND o.status = 'paid') —— 推荐用于关联存在性判断;不依赖子查询返回值内容,只看是否“有匹配行”,通常比 IN 更快,尤其外层大表、内层可走索引时
  • 避免 NOT IN (SELECT ...):只要子查询结果里有一个 NULL,整个条件恒为 FALSE,结果为空——这是线上事故高发点

FROM 子句中用子查询当临时表(派生表)

MySQL 要求所有子查询在 FROM 中必须有别名,否则报错 Every derived table must have its own alias。适合做中间聚合或筛选后再连接。

  • SELECT t.name, t.cnt FROM (SELECT dept_id, count(*) AS cnt FROM employees GROUP BY dept_id) AS t JOIN departments d ON t.dept_id = d.id
  • 子查询里的字段不能直接用外层别名引用(如 t.dept_id 在子查询内部不可见)
  • MySQL 5.7 及以前,派生表默认物化(即先执行完再参与连接),可能导致无法使用索引;8.0+ 引入“派生合并优化”,可将子查询“展开”进外层,但需满足条件(如无 GROUP BYDISTINCT 等)
  • 复杂逻辑建议改用 CTE(WITH),可读性和优化器支持更好(MySQL 8.0+)

UPDATE / DELETE 语句里嵌套子查询的限制

MySQL 不允许在 UPDATEDELETEWHERE 中直接引用目标表,否则报错 You can't specify target table for update in FROM clause。这是新手最容易卡住的地方。

  • 错误写法:DELETE FROM users WHERE id IN (SELECT user_id FROM logs WHERE action = 'spam') —— 如果 logsusers 是同一张表,就挂了
  • 绕过方法:把子查询再包一层 FROM 子查询(即做成派生表),例如:DELETE FROM users WHERE id IN (SELECT id FROM (SELECT user_id AS id FROM logs WHERE action = 'spam') AS tmp)
  • 更稳妥的做法是用 JOIN 重写:DELETE u FROM users u JOIN logs l ON u.id = l.user_id WHERE l.action = 'spam'
  • 注意:UPDATE 同理,且涉及多表更新时,务必确认 JOIN 条件能准确定位,否则可能误删/误改

子查询看着简单,但 MySQL 对它的执行计划干预有限,尤其多层嵌套时容易触发全表扫描。真正要注意的不是“怎么写出来”,而是“执行时扫了多少行”——开 EXPLAINtyperows 列,比语法正确更重要。

text=ZqhQzanResources