延迟关联(Deferred Join)必须严格满足索引结构、子查询裁剪和执行顺序控制,否则性能反而更差;MySQL 5.7+需用STRAIGHT_JOIN强制子查询先执行并走覆盖索引,且索引须按WHERE、ORDER BY、主键字段严格排序,缺一不可。

直接说结论:延迟关联(Deferred Join)不是“加个JOIN就能提速”,它必须配合严格索引结构、子查询裁剪和执行顺序控制,否则性能可能比原SQL更差。
为什么INNER JOIN延迟关联在MySQL 5.7+里容易失效
MySQL 5.7优化器不会自动把子查询下推到索引扫描阶段,它可能把SELECT id FROM t ORDER BY created_at DESC LIMIT 100000, 10物化成临时表,导致type=ALL、Using filesort全量扫描。这不是写法错,是优化器行为限制。
- 子查询必须只查
id(或主键),不能带任何业务字段,否则无法走覆盖索引 - 联合索引必须按
(WHERE条件字段, ORDER BY字段, 主键)严格排序,缺一不可 - 如果
ORDER BY created_at DESC, id DESC,索引也必须是(created_at DESC, id DESC),升序索引无法复用 -
EXPLAIN中子查询的Extra字段必须含Using index,否则优化失败
延迟关联SQL必须这样写才生效
正确结构是两层分离:子查询只定位ID,外层只负责装载。任何混写都会让优化失效。
面向 CRM 系统的 MySQL 8 数据库模式设计。当用户需要为客户关系管理(CRM)系统设计、评审、优化或生成数据库模式时,应使用此项技能。
SELECT o.* FROM orders o INNER JOIN ( SELECT id FROM orders WHERE status = 1 ORDER BY created_at DESC, id DESC LIMIT 100000, 20 ) AS tmp ON o.id = tmp.id ORDER BY o.created_at DESC, o.id DESC;
- 子查询里
WHERE status = 1必须命中索引最左前缀,否则过滤失效 - 外层
ORDER BY不能省——子查询排序不保证外层输出顺序,尤其MySQL 8.0+可能重排 - 若
created_at允许NULL,索引需声明NOT NULL,否则排序不稳定,分页结果跳变 - 避免隐式类型转换:
status = '1'(字符串)会绕过整型索引,必须写status = 1
MySQL 5.7必须加STRAIGHT_JOIN强制连接顺序
5.7默认可能把主表orders当作驱动表,导致子查询被当成被驱动表物化。必须显式干预:
SELECT STRAIGHT_JOIN o.* FROM orders o INNER JOIN ( SELECT id FROM orders WHERE status = 1 ORDER BY created_at DESC, id DESC LIMIT 100000, 20 ) AS tmp ON o.id = tmp.id ORDER BY o.created_at DESC, o.id DESC;
-
STRAIGHT_JOIN强制子查询先执行,且结果集作为驱动表 - 子查询别名
tmp必须放在JOIN右侧,主表o必须在左侧 - 不加
STRAIGHT_JOIN时,EXPLAIN可能显示rows为全表行数,说明优化未生效
真正卡住人的从来不是语法,而是索引字段顺序、NULL处理、类型一致性这些细节。一旦漏掉其中一项,延迟关联就退化成普通JOIN,甚至更慢——因为多了一次子查询临时表构建开销。


















