ora-14611本质是索引键字节长度超限(767/3072字节),非值非法;根因多为varchar2分区键在al32utf8下中文占3字节致实际键长超标,尤以唯一本地索引最危险。
ora-14611 错误本质是索引键长度超限,不是数据内容问题
ora-14611 的完整报错通常是 ora-14611: invalid value for index partition key,但它**极少由“值非法”触发**,真实根因几乎总是:某个被用作分区键(或本地索引键)的字段,在某一分区中实际生成的索引键长度超过了 oracle 对该索引段的限制(如 767 字节或 3072 字节)。尤其常见于 varchar2 字段参与分区键 + 使用了前缀索引或函数索引时。
错误发生时,SQL 可能看起来完全合法,比如插入一条普通记录,但 Oracle 在构建本地索引条目时发现:该记录在当前分区对应的索引段里,键值编码后超出物理限制,于是抛出这个误导性提示。
检查索引键长度是否真超限,别只看字段定义
不能只查 DESCRIBE table_name 或 USER_TAB_COLUMNS 看字段长度。必须计算「实际索引键字节数」,它取决于:
- 字段字符集(AL32UTF8 下一个中文占 3 字节,不是 1)
- 是否启用
DB_BLOCK_SIZE和MAX_STRING_SIZE=EXTENDED - 索引类型:普通 B-tree 索引默认限制 767 字节;启用
COMPATIBLE >= 12.2且DB_BLOCK_SIZE=8K时,INVISIBLE或函数索引可能更敏感 - 是否为本地索引(
LOCAL):每个分区独立建索引段,键长限制按分区单独校验
快速验证方法:
SELECT column_name, data_type, data_length, char_length, (data_length * 3) AS max_utf8_bytes FROM user_tab_columns WHERE table_name = 'YOUR_TABLE' AND column_name IN ( SELECT column_name FROM user_ind_columns WHERE index_name = 'YOUR_INDEX' );
如果任一 max_utf8_bytes > 767(或 3072),且该字段又参与了分区键或索引键,则极可能触发 ORA-14611。
修复方案优先级:从低侵入到重建
不建议直接删索引重来。按风险和效果排序:
- 确认并收缩字段内容:用
TRIM、SUBSTR截断过长值,或业务层加校验 - 改用前缀索引(仅适用于非唯一索引):
CREATE INDEX idx ON t(col) SUBSTR(col, 1, 255) - 将字段类型改为
CLOB并移出索引键 —— 但注意:CLOB 列不能作为分区键,也不能出现在普通 B-tree 索引中 - 对本地索引执行
ALTER INDEX ... REBUILD PARTITION p_name,可重置该分区索引段结构,有时能绕过临时碎片导致的误判 - 终极手段:重建分区表,显式指定
PARTITION BY RANGE时用更短的键列,或改用全局索引(GLOBAL)避免 per-partition 键长校验
容易忽略的关键点:唯一索引 + 分区键组合最危险
当主键或唯一约束依赖本地索引(即 PRIMARY KEY ... LOCAL)时,Oracle 会强制要求「分区键必须包含在唯一键中」。这导致两个风险叠加:
- 分区键字段本身被强制纳入索引键 → 放大键长
- 唯一性校验需跨分区扫描 → 若某分区索引键超限,整个 DML 就失败,报错却只显示 ORA-14611
此时不能只修索引,必须检查 USER_CONSTRAINTS 中对应约束的 GENERATED 列是否为 GENERATED NAME,再查 USER_IND_COLUMNS 确认索引键组成。很多团队卡在这里数小时,就因为没意识到「主键定义」和「索引物理实现」是两层东西。











