Oracle不支持STRAIGHT_JOIN,需用/+ leading() /等hint干预驱动表顺序,且仅对同级FROM表生效;ANSI JOIN+hint需12c+,旧版建议用传统逗号语法;LEFT JOIN性能问题多因索引缺失、隐式转换或统计信息过期。

PL/SQL里没有STRAIGHT_JOIN,别白费劲找语法
Oracle数据库不支持STRAIGHT_JOIN,MySQL那一套在PL/SQL中直接无效。你查文档、试语法、加注释都无用——Oracle用的是完全不同的优化机制,比如/*+ leading() */或/*+ use_nl() */这类hint来干预执行顺序。
用leading() hint强制驱动表顺序,但必须写对位置
Oracle的/*+ leading(t1 t2 t3) */会强制按括号内表的物理顺序作为JOIN驱动链,但它只对当前查询块生效,且要求所有被指定的表必须出现在同一级FROM中(不能藏在子查询或视图里)。
- 错误写法:
SELECT /*+ leading(o c) */ ... FROM orders o LEFT JOIN customers c ON ...—— Oracle 12c+才真正支持ANSI JOIN + hint组合,旧版本可能忽略 - 稳妥写法:改用传统逗号语法+WHERE连接,再加hint:
SELECT /*+ leading(c o i) */ c.name, o.order_id FROM customers c, orders o, order_items i WHERE o.customer_id(+) = c.id AND i.order_id(+) = o.id - 如果用了CTE(WITH子句),hint必须放在外部主查询,CTE内部加无效
LEFT JOIN右表全表扫描?先看ON字段有没有索引,再看类型是否一致
Oracle对LEFT JOIN右表不走索引,90%是因为ON条件字段缺失索引或存在隐式转换。比如左表id是NUMBER,右表关联字段却是VARCHAR2,Oracle会自动加TO_NUMBER(),导致索引失效。
- 检查执行计划中右表的
access_predicates列:若显示"T2"."REF_ID"=:B1说明走了索引;若显示"T2"."REF_ID"=TO_NUMBER(:B1)就是隐式转换 - 复合索引要覆盖全部ON字段+WHERE中可能用到的过滤字段,例如
ON t2.a_id = t1.id AND t2.status = 'active',索引应建为(a_id, status),而非仅(status) - 统计信息过期也会让优化器误判,定期跑
DBMS_STATS.GATHER_TABLE_STATS,尤其在大批量导入后
多个LEFT JOIN同一张表(如附件表)时,优先考虑UNPIVOT或聚合子查询
像LEFT JOIN tbl_file f0 ON f0.obj_id = safe.id AND f0.file_class = 0连六次,本质是“一行转多列”,这不是JOIN场景,是典型的宽表建模反模式。Oracle执行时会为每个fN生成独立嵌套循环,IO和CPU压力成倍放大。
- 更高效做法:用单次LEFT JOIN +
CASE WHEN聚合,例如MAX(CASE WHEN f.file_class = 0 THEN f.file_path END) AS file0_path - 如果file_class取值固定且不多,可提前用
UNPIVOT预处理tbl_file,把6行变1行,再JOIN一次 - 实在要保留多JOIN结构,至少给
tbl_file(obj_id, file_class)建唯一组合索引,避免每次JOIN都扫全表
NESTED LOOPS OUTER配TABLE ACCESS FULL,第一反应不该是调hint,而是问:这个逻辑能不能用单次JOIN+条件聚合替代?——这点比任何hint都关键。


















