sql中and优先级高于or,未加括号时where a=1 or b=2 and c=3等价于a=1 or (b=2 and c=3),而非(a=1 or b=2) and c=3,易致漏数据;所有and/or混用必须显式括号分组,子查询参与复合逻辑时需注意null、单值约束及性能陷阱。

子查询里混用 AND 和 OR 为什么总出错
因为 SQL 运算符优先级默认是 AND 高于 OR,没加括号时,WHERE a=1 OR b=2 AND c=3 实际等价于 WHERE a=1 OR (b=2 AND c=3),而不是你直觉想表达的 (a=1 OR b=2) AND c=3。这种隐式分组极易导致漏数据或误过滤。
实操建议:
- 所有含
AND和OR混用的条件,必须显式用括号包裹逻辑单元,哪怕只有一处 - ThinkPHP 等 ORM 的数组写法(如
array(array('gt',3),array('lt',10), 'or'))会自动加括号,但原生 SQL 不会 - 用 EXPLAIN 查看执行计划,确认实际生效的过滤条件是否与预期一致
在 WHERE 中嵌套子查询并带复合逻辑
子查询本身可以返回单值、多值或整张临时表,但和外部条件组合时,容易因类型不匹配或 NULL 处理出问题。比如 IN 子句遇到子查询返回 NULL,整个条件会变成未知(UNKNOWN),结果被过滤掉。
实操建议:
- 用
IN (SELECT id FROM t WHERE status = 1)前,先确认子查询不会返回NULL;否则加WHERE id IS NOT NULL - 需要“存在且满足某组合条件”时,优先用
EXISTS而非IN,它天然规避 NULL 陷阱 - 若子查询要参与
AND/OR组合(如status = 1 AND score > (SELECT AVG(score) FROM t2)),确保子查询只返回单值,否则报错
FROM 子句里的子查询如何支持多条件过滤
把子查询当临时表用(即 FROM (SELECT ...) AS tmp)时,它的字段不能直接被外部 WHERE 引用,除非别名已定义。更关键的是,如果子查询内部已有复杂条件,再在外层叠加,可能让优化器放弃索引。
实操建议:
- 子查询中尽量提前过滤,比如
FROM (SELECT * FROM orders WHERE created_at > '2025-01-01') AS recent_orders,别留到外层再筛 - 外层
WHERE若需引用子查询字段,必须确保该字段在子查询的SELECT列表中显式写出 - MySQL 8.0.14 之前,
FROM子查询不支持关联子查询(即不能引用外层表字段),升级前要避免类似FROM (SELECT * FROM t2 WHERE t2.id = t1.ref_id)写法
复合条件 + 子查询的性能踩坑点
最隐蔽的问题不是语法错,而是执行慢——尤其当子查询在 WHERE 或 HAVING 中被反复执行(比如对每行都跑一次),或者子查询结果集太大却没限制。
实操建议:
- 用
EXISTS替代IN查询大表,通常更快;反过来,小结果集用IN更简洁 -
HAVING COUNT(*) > (SELECT COUNT(*) FROM ...)这类写法会让子查询执行多次,应改用 JOIN 预聚合 - 子查询结果若超过几千行,考虑物化为临时表(
CREATE TEMPORARY TABLE),再加索引
子查询嵌套层级越深,SQL 可读性和可维护性下降越快;真正难的不是写出来,而是别人(或一周后的你)能快速看懂哪一层在过滤什么、NULL 怎么处理、索引有没有生效。










