不能只写use_hash或leading,必须配对使用并严格校验执行计划;选连接算法需看连接条件类型、数据量级和索引情况;leading是use_nl生效前提;不同数据库hint支持差异大,须按版本和语法规范使用。

直接说结论:不能只写 USE_HASH 或 LEADING 就完事,必须配对使用、严格校验执行计划,否则大概率被优化器忽略或静默失效。
USE_NL / USE_HASH / USE_MERGE 怎么选才不翻车
选错算法是 Hint 失效的最常见原因。关键看三件事:连接条件类型、表数据量级、连接列是否有高效索引。
-
USE_NL(t2)适合驱动表小(比如过滤后只剩几百行)、内表t2的连接列有ref或eq_ref索引——这时嵌套循环真正快;如果t2连接列没索引,USE_NL只会让大表被反复全扫 -
USE_HASH(t2, t3)要求两表都大、等值连接、内存够——但注意:MySQL 8.0.20+ 不支持该 Hint,写了也当注释;Oracle/PostgreSQL 才认;SQL Server 用OPTION (HASH JOIN) -
USE_MERGE(t3, t4)基本只用于非等值条件(如BETWEEN、>=),且必须确保t3和t4已按连接列排序;否则优化器会先加隐式排序,反而更慢
LEADING 是 USE_NL 生效的前提,不是可选项
LEADING 不是“建议顺序”,而是强制 JOIN 树结构的锚点。没有它,USE_NL(t2) 只表示“如果 t2 是内表,就用 NL”,但优化器仍可能把 t2 当外表——结果就是 Hint 白写。
- 正确写法:
/*+ LEADING(t1 t2) USE_NL(t2) */→ 明确t1是外表、t2是内表,且t1先访问 - 多层嵌套:
/*+ LEADING(t1 (t2 t3)) USE_NL(t3) */→ 先算t2和t3的 JOIN(顺序不定),再用t1驱动该结果;括号里不指定内外,只表示组合 - 表名必须用别名,且别名已在
FROM中定义;写原表名或带 schema 的名字(如schema.t1)都会导致 Hint 失效
STRAIGHT_JOIN 和 leading() 别混用,适用场景完全不同
STRAIGHT_JOIN 是 MySQL 专属粗粒度控制,leading() 是 Oracle/PG 等支持的细粒度编排,二者语法、生效机制、适用范围都不兼容。
-
STRAIGHT_JOIN只能放在SELECT关键字后、FROM前,且仅对SELECT有效;UPDATE STRAIGHT_JOIN直接报错 -
STRAIGHT_JOIN会完全禁用优化器重排,适合你已确认驱动表加 WHERE 后只剩极少量行(比如最近一小时订单),且右表连接列有索引的场景 -
leading()支持嵌套括号、多表分组、子查询提升后的逻辑重组,但必须配合EXPLAIN看实际执行计划里是否真出现NESTED LOOPS或HASH JOIN字样——光看到Join Filter不算数
加了 Hint 却没生效?先查这三处
Hint 不等于执行计划变更。很多问题出在“以为生效了”,其实优化器根本没理你。
- 数据库版本不支持:旧版 MySQL 不识别
/*+ USE_HASH() */,只会当普通注释跳过;得查文档确认当前版本是否支持对应 Hint 语法 - 执行计划没验证:必须用
EXPLAIN FORMAT=TREE(MySQL 8.0.16+)或EXPLAIN PLAN(Oracle)看输出中是否明确出现NESTED LOOP、HASH JOIN等字样,而不是只看type列 - 隐式排序或类型转换破坏前提:比如
ON a.id = CAST(b.id AS CHAR)会让索引失效,USE_NL就失去意义;USE_MERGE要求排序,但WHERE条件若含函数,可能导致排序无法复用
最常被忽略的一点:Hint 只影响单次 JOIN 操作,而一个查询里可能有多个 JOIN 步骤;你指定了 USE_NL(t2),但后续 t2 和 t3 的 JOIN 仍由优化器决定——得逐层检查执行计划里的每个节点。











