Oracle 11g中绑定变量导致分区裁剪失效,因CBO在硬解析时无法推导分区范围,只能全分区扫描;需改用字面量、启用ACS、确保元数据匹配等显式手段绕过。
绑定变量让CBO无法静态推导分区范围
oracle 11g 的分区裁剪必须在硬解析阶段就确定访问哪些分区,而绑定变量的值在解析时不可见。cbo 只能按“最保守”策略处理——全分区扫描。这不是 bug,是设计行为:优化器不假设运行时传入的值会落在某个特定分区,除非有额外机制辅助推导。
常见现象:PARTITION RANGE ALL 出现在执行计划中,pstart/pstop 显示为 ALL,哪怕实际每次只查一个分区;A-Rows 极低但 Buffers 极高,说明物理读了大量无关分区。
- 即使 SQL 写成
WHERE dt = :p_dt,只要:p_dt是绑定变量,11g 默认不启用自适应游标共享(ACS),不会为不同值生成多个 child cursor -
v$sql中对应 SQL 的IS_BIND_SENSITIVE、IS_BIND_AWARE、IS_SHAREABLE若非全为Y,说明 ACS 未激活,裁剪必然失效 - 11g 默认
_optim_peek_user_binds = TRUE,但仅对硬解析时的首次值“窥视”,后续相同 SQL 文本若未触发 ACS,仍复用原计划
为什么加 hint 也不一定管用
给 SQL 加 /*+ USE_NL(t) */ 或 /*+ INDEX(t idx_dt) */ 不影响分区裁剪逻辑。裁剪发生在访问路径选择前,属于更底层的“分区扫描范围决策”。hint 无法绕过 CBO 对绑定变量值不可知这一事实。
唯一能强制裁剪的 hint 是 /*+ PARTITION(t, p202401) */,但它要求你提前知道分区名,且只适用于单分区场景,无法适配动态日期范围。
-
/*+ PARALLEL(t, 4) */会生效,但前提是裁剪已发生;否则并行只是加速全扫,反而加重资源争用 -
/*+ DYNAMIC_SAMPLING(t 6) */在 11g 中对分区裁剪几乎无效——动态采样 level 6 仍不保证跨分区抽样,且仅用于统计信息缺失时补位,不改变绑定变量的解析逻辑 - 试图用
/*+ OPT_PARAM('_optim_peek_user_binds' 'true') */无意义:该参数已是默认值,且 peek 行为不等于“推导分区”
11g 下真正可用的绕过方案
不能依赖运行时行为修正,得从 SQL 结构和执行上下文入手。11g 缺乏后续版本的智能绑定处理能力,必须显式暴露分区边界。
- 改用字面量重写关键查询段,例如把
WHERE dt = :p_dt拆成WHERE dt = TO_DATE('2024-01-01', 'YYYY-MM-DD')(配合应用层拼接,注意 SQL 注入防护) - 对固定范围场景,用
WHERE dt BETWEEN :p_start AND :p_end+DBMS_STATS.LOCK_TABLE_STATS锁定分区统计信息,再配合GRANULARITY => 'PARTITION'收集,能让 CBO 更倾向裁剪(但仍有失败风险) - 启用
ALTER SESSION SET "_optimizer_adaptive_cursor_sharing" = TRUE(需 11.2.0.2+),并确保cursor_sharing = SIMILAR(注意:该参数在 12c 已废弃,11g 中仍有效) - 避免在 WHERE 中混用绑定变量与函数,例如
WHERE TRUNC(dt) = :p_date—— 即使:p_date是 DATE 类型,TRUNC也阻断裁剪
最容易被忽略的元数据陷阱
很多“绑定变量失效”其实是表定义或统计信息层面的问题被误判为变量问题。11g 对元数据异常更敏感。
- 检查
dba_part_tables中partitioning_type是否为RANGE或LIST,partition_key_columns拼写是否与 SQL 中完全一致(大小写敏感) - 确认分区键列类型与绑定变量类型严格匹配:DATE 分区不能传入
'2024-01-01 00:00:00'字符串,NUMBER 分区不能传入浮点数 - 如果表上有子分区,
dbms_xplan.display_cursor输出中Partitions accessed字段可能显示1/128这类比例,但Starts为 128 —— 说明只裁剪到子分区一级,主分区仍全扫,根源常在子分区键未被 WHERE 直接约束











