子查询必须用括号包住,否则直接报错
MySQL 要求所有子查询必须出现在圆括号中,哪怕它只是
SELECT语句的一部分。不加括号会触发
ERROR 1064 (42000):语法错误。
WHERE id IN (SELECT user_id FROM logs)✅ 正确
WHERE id IN SELECT user_id FROM logs❌ 报错 相关子查询也一样:
WHERE salary > (SELECT AVG(salary) FROM employees e2 WHERE e2.dept = e1.dept)
WHERE 中的子查询只能返回单列,多列会报错
在
WHERE、
HAVING或
ON子句里使用子查询时,MySQL 只允许它返回**一列**(即使你只用其中一列)。如果子查询写了
SELECT name, id FROM users,哪怕外层只比对
id,也会报
ERROR 1241 (21000): Operand should contain 1 column(s)。 正确写法:
WHERE dept_id IN (SELECT id FROM departments WHERE active = 1)错误写法:
WHERE dept_id IN (SELECT id, name FROM departments)若需多字段关联,改用
JOIN或
EXISTS(见下一条)
EXISTS 比 IN 更适合检查存在性,尤其子查询结果可能含 NULL
用
IN判断“是否存在某记录”时,如果子查询结果包含
NULL,整个条件会恒为
UNKNOWN,导致查不到任何数据——这是隐式类型转换和三值逻辑惹的祸。
SELECT * FROM orders WHERE customer_id IN (SELECT id FROM customers WHERE city = 'Beijing')—— 若子查询返回
(1, 2, NULL),整条
IN判断失效
SELECT * FROM orders WHERE EXISTS (SELECT 1 FROM customers WHERE customers.id = orders.customer_id AND city = 'Beijing')—— 不受
NULL影响,语义更清晰
EXISTS通常也更快,因为找到第一条匹配就停止扫描
UPDATE / DELETE 不能直接引用目标表,得用派生表绕过
MySQL 禁止在
UPDATE或
DELETE的子查询中直接读取正在修改的同一张表,否则报
ERROR 1093 (HY000): You can't specify target table 'xxx' for update in FROM clause。 错误示例:
DELETE FROM users WHERE id IN (SELECT id FROM users WHERE created_at正确解法:把子查询包装成派生表(即加一层
SELECT * FROM (...) AS tmp):
DELETE FROM users WHERE id IN (SELECT id FROM (SELECT id FROM users WHERE created_at注意别名
AS tmp是必需的,MySQL 8.0+ 仍要求显式别名
实际写嵌套查询时,最易被忽略的是子查询的执行时机和 NULL 处理逻辑——它们不会像函数调用那样“先算完再代入”,而是在每行外层数据上动态执行,并受当前行上下文影响。
