分区裁剪需显式在join条件中包含分区字段且类型一致、无函数包裹;left join时右表分区条件必须写在on中,否则退化为inner join且裁剪失效。

JOIN条件里必须显式包含分区字段
分区裁剪不会因为表被分区就自动生效,它依赖优化器从查询逻辑中静态推导出可跳过的分区。如果JOIN只写user_id = user_id,而两边都没提dt、event_time这类分区键,那无论左表右表都可能全扫。
常见错误是以为“JOIN on 主键 + WHERE 时间范围”就够了——其实右表的分区裁剪往往卡在这一步。
- ✅ 正确:LEFT JOIN t2 ON t1.user_id = t2.user_id AND t1.dt = t2.dt(两边
dt等值对齐) - ✅ 正确:INNER JOIN t2 ON t1.user_id = t2.user_id AND t1.event_time BETWEEN t2.start_time AND t2.end_time(区间匹配,且
t2.start_time是其分区键) - ❌ 错误:LEFT JOIN t2 ON t1.user_id = t2.user_id WHERE t2.dt = '2026-08-01'(
WHERE过滤右表,在LEFT JOIN下会退化为INNER JOIN语义,且裁剪可能失效) - ❌ 错误:JOIN t2 ON t1.user_id = t2.user_id AND DATE(t1.dt) = DATE(t2.dt)(函数包裹直接禁用裁剪)
LEFT JOIN时右表分区条件必须写在ON里
LEFT JOIN的语义决定了左表全保留,右表按条件匹配。但数据库优化器只有在ON子句里看到右表的分区字段约束,才敢把它当作“可裁剪的驱动维度”。一旦挪到WHERE里,就变成“必须非空”,等于强制INNER JOIN,而且裁剪上下文丢失。
典型现象:执行计划里右表显示PARTITION RANGE ALL或STARTS值远大于实际分区数。
- ✅ 正确:
LEFT JOIN t2 ON t1.dt = t2.dt AND t2.dt = '2026-08-01' - ✅ 正确:
LEFT JOIN t2 ON t1.user_id = t2.user_id AND t2.dt BETWEEN t1.dt AND DATE_ADD(t1.dt, INTERVAL 7 DAY)(前提是t2.dt是分区键,且有对应索引) - ❌ 错误:
LEFT JOIN t2 ON t1.user_id = t2.user_id WHERE t2.dt = '2026-08-01' - ⚠️ 注意:即使
t2是小表,没写对位置照样全扫——裁剪不是看数据量,是看逻辑表达是否可推导
类型一致和无函数是硬性前提
分区裁剪是优化器在解析阶段做的静态判断,任何让字段“不可比”的操作都会让它放弃推理。类型不匹配、隐式转换、函数包装,三者任一出现,裁剪就断了。
- ✅
t1.dt STRING匹配WHERE t1.dt = '2026-08-01'(字面量带单引号) - ❌
t1.dt STRING匹配WHERE t1.dt = 20260801(数字字面量触发隐式转换) - ❌
WHERE DATE(t1.dt) = '2026-08-01'(DATE()函数彻底关闭裁剪能力) - ❌
WHERE t1.dt + INTERVAL 1 DAY = '2026-08-02'(表达式破坏静态边界识别) - ⚠️ 小陷阱:Hive/Spark 中
STRINGvsINT分区类型混用很常见,但只要JOIN两边不一致,裁剪就失效
确认裁剪是否真生效,只信EXPLAIN里的ACCESS PREDICATES
别靠“查得快”或“感觉只用了几个分区”来判断——唯一可信的是执行计划里明确写出分区过滤条件的位置和范围。
重点关注ACCESS PREDICATES(真正用于定位分区的条件)和FILTER PREDICATES(扫描后过滤,已晚);同时核对PARTITION RANGE是否为具体值而非ALL或ITERATOR。
- ✅ 健康信号:
ACCESS PREDICATES (\"dt\" = '2026-08-01')+PARTITION RANGE SINGLE - ❌ 危险信号:
FILTER PREDICATES (\"dt\" = '2026-08-01')+PARTITION RANGE ALL - ⚠️ 隐蔽坑:有些引擎(如Oracle)需开
DBMS_XPLAN.DISPLAY_CURSOR并检查STARTS列——若值远大于你预期的分区数,说明裁剪没落地











