跨分区查询慢的主因是未启用partition-wise join,需确保两表同策略分区、分区键完全一致、连接条件直写分区键且均建本地索引;验证需查other_xml中pwj="true"、并行进程分组及disk_reads下降超40%。

跨分区查询慢,不是索引问题而是执行计划没走Partition Wise Join
Oracle分区表跨分区查询(比如查某用户近半年所有订单)真正卡住的点,往往不是数据量大,而是执行计划里压根没出现PARTITION-WISE JOIN。它一不生效,两张大表连接就退化成全量HASH JOIN或NESTED LOOPS,数据在各进程间疯狂搬运(“洗牌”),I/O和内存压力陡增。
- 必须两张表都用相同策略分区:同为
RANGE或同为LIST,不能混用; - 分区键列名、数据类型、表达式完全一致——
order_date对order_date可以,order_date对TRUNC(order_date)直接失效; - 边界定义逻辑等价:比如都按月切分,且
VALUES LESS THAN的值必须完全相同(包括时区、NLS设置); - 连接条件必须直写分区键:
o.order_date = c.order_date行,TRUNC(o.order_date) = TRUNC(c.order_date)不行; - 本地索引是前提:其中一张表用了
GLOBAL INDEX,优化器就无法确认分区边界与数据分布的一致性,PWJ自动放弃。
执行计划里没看到PARTITION-WISE字样?先查这三个地方
别只盯着EXPLAIN PLAN输出文字,很多团队误以为“写了JOIN条件=就一定走PWJ”,结果发现DISK_READS没降反升。真正要验证,得看底层行为是否分区对齐:
- 查
PLAN_TABLE的OTHER_XML字段:partition_view_enabled="yes"且pwj="true"才算真生效; - 会话级强制开启分区扫描:
ALTER SESSION SET "_px_partition_scan_threshold" = 0;(仅测试环境用); - 查
V$PX_PROCESS和V$SESSION_LONGOPS:并行进程是否按P000~P007分组工作,且每组对应不同分区号; - 对比
V$SQLAREA中DISK_READS和BUFFER_GETS:真正生效的PWJ会让这两个值下降40%以上(前提是连接结果集本身不大)。
本地索引建错位置,跨分区查询照样拖垮性能
很多人建了LOCAL索引就以为万事大吉,但实际中常踩两个坑:一是索引列选错,二是统计信息过期。跨分区查询如果涉及非分区键字段(比如user_id),而你又没在这字段上建本地索引,那每个分区都要全扫一遍再合并,性能比单表还差。
- 高频按
user_id查全量流水 → 在user_id上建LOCAL索引,语句是:CREATE INDEX idx_user ON t(user_id) LOCAL;; - 千万别在分区键上重复建索引:比如表已按
create_dateRANGE分区,再建CREATE INDEX idx_dt ON t(create_date) LOCAL意义不大; - 统计信息必须按分区粒度更新:
EXEC DBMS_STATS.GATHER_TABLE_STATS('SCHEMA_NAME', 'TABLE_NAME', GRANULARITY => 'ALL');; - 检查是否启用增量统计:
SELECT incremental FROM user_tab_partitions WHERE table_name = 'TABLE_NAME';,没开就补上INCREMENTAL => TRUE。
全局索引不是救星,反而让跨分区DDL变成定时炸弹
有些团队想绕开PWJ的苛刻条件,直接给关键字段建GLOBAL INDEX,结果发现查得快了,但每月DROP PARTITION操作卡住5分钟,业务报警不断。根本原因是全局索引跨所有分区,DDL时必须重建整个索引结构,期间锁表、阻塞DML。
- 只有当跨分区查询极多、且分区极少变动(比如年表,每年只加1次分区)时,才考虑全局索引;
- 一旦用了全局索引,
DROP PARTITION前必须先置为UNUSABLE:ALTER INDEX idx_global UNUSABLE;,否则索引直接失效; - 重建必须带
UPDATE GLOBAL INDEXES子句,否则索引状态永远是UNUSABLE; - 更现实的做法:用
EXCHANGE PARTITION把旧数据导出到历史表,避免DROP带来的锁风险。
跨分区查询真正的复杂点不在SQL怎么写,而在分区结构、索引策略、统计信息三者是否咬合严密。少一个齿轮,整个优化链就空转。











