应避免在join字段或where条件中对列使用函数,如upper(name),否则导致索引失效、全表扫描;建议预存规范值并建索引,改写嵌套子查询为join,逐层精简select字段,将order by和limit置于最外层。

WHERE 条件里别在 JOIN 字段上用函数
数据库没法用索引加速 WHERE UPPER(name) = 'JOHN' 这类写法,尤其当它出现在 JOIN 的关联条件或外层过滤中,会直接让嵌套查询退化成全表扫描。真实场景里,比如 LEFT JOIN user_info ON UPPER(u.name) = UPPER(i.name),两个表都得先算一遍 UPPER(),再比对——数据量一过十万,响应就卡住。
实操建议:
- 提前把常用字段的规范值存为新列(如
name_upper),并加索引 - 用
COLLATE utf8mb4_0900_as_cs(MySQL 8.0+)或COLLATE SQL_Latin1_General_CP1_CI_AS(SQL Server)替代函数做大小写不敏感比较 - 确认执行计划里
type是ref或eq_ref,不是ALL或index
嵌套子查询优先改写成 JOIN
像 SELECT * FROM orders WHERE customer_id IN (SELECT id FROM customers WHERE region = 'CN') 这种结构,在 PostgreSQL 和 MySQL 8.0 以前常被优化器误判为“依赖子查询”,导致外层每行都执行一次内层——实际变成 N×M 次扫描。
实操建议:
- 手动改写为
INNER JOIN customers c ON o.customer_id = c.id WHERE c.region = 'CN' - 如果子查询带
LIMIT或GROUP BY无法直接转 JOIN,加/*+ MATERIALIZE */(Oracle)或WITHCTE 强制物化(PostgreSQL/SQL Server) - MySQL 用户注意:5.7 默认不展开
IN (subquery),升级到 8.0 并开启optimizer_switch='semijoin=on'
多层嵌套时慎用 SELECT *
SELECT * FROM (SELECT * FROM (SELECT * FROM t1 JOIN t2 ...) t23) t34 看似省事,但每层都会把所有字段带下去,IO 和内存压力翻倍。更糟的是,外层没用到的字段,优化器也未必能剪掉——特别是用了视图或 CTE 包裹后。
实操建议:
- 每一层只
SELECT下一层真正需要的字段,尤其是 JOIN 键和过滤字段 - 给中间结果集起明确别名,避免
column ambiguously defined错误 - 在 PostgreSQL 中,用
EXPLAIN (ANALYZE, BUFFERS)看Shared Hit Blocks是否异常高,判断是否因冗余字段拖慢缓冲区命中
ORDER BY + LIMIT 放在外层,别塞进子查询
把 ORDER BY create_time DESC LIMIT 10 写在最内层子查询里,看起来能减少数据量,但多数数据库(MySQL 5.7、SQL Server)会在嵌套中丢失排序上下文,最终结果可能错乱或性能更差——因为优化器无法下推 LIMIT 到物理扫描阶段。
实操建议:
- 确保
LIMIT和ORDER BY出现在最外层SELECT,且排序字段有索引 - 如果必须分页查关联数据(如“每个分类最新3条商品”),用
ROW_NUMBER() OVER (PARTITION BY cat_id ORDER BY create_time DESC)替代多层子查询 - MySQL 用户要警惕
sql_mode含ONLY_FULL_GROUP_BY时,GROUP BY嵌套中混用非分组字段会报错,别靠关模式绕过
嵌套层级一深,执行计划就容易失真;索引是否生效、字段是否被剪枝、排序能否下推——这些都不是看语句长得像不像优化过就能判断的。上线前一定拿真实数据量跑 EXPLAIN,别信开发环境那几千条测试数据的响应时间。










