全局唯一索引必须以分区键为第一列,否则报ORA-14038;因Oracle需通过前缀映射索引分区与表分区,确保DML及分区维护时能准确定位并维护索引一致性。
全局唯一索引必须包含分区键,且必须是前缀索引;否则直接报 ora-14038 错误。
为什么全局索引必须带前缀(即分区键作为引导列)
Oracle 强制要求 GLOBAL 分区索引只能是前缀索引,本质是为了保证索引分区与表数据分布的可映射性。因为全局索引的每个分区可能覆盖多个表分区,若不以分区键开头,Oracle 无法在 DML 或分区维护(如 ALTER TABLE ... DROP PARTITION)时快速定位受影响的索引条目,进而无法维护索引一致性。
常见错误现象:
- 执行
CREATE INDEX ... GLOBAL PARTITION BY RANGE(...)但索引列不含分区键 → 报错:ORA-14038: GLOBAL 分区索引必须加上前缀 - 索引列含分区键但不在第一位置(如
CREATE INDEX idx ON t(name, id) GLOBAL...,而id是表的分区键)→ 同样报ORA-14038
创建带分区键的全局唯一索引的正确写法
关键点:分区键必须是索引定义中的**第一个列**,且 UNIQUE 约束才能生效(否则唯一性无法跨分区保证)。
假设表 sales 按 sale_date 范围分区:
CREATE TABLE sales ( sale_id NUMBER PRIMARY KEY, sale_date DATE, amount NUMBER ) PARTITION BY RANGE (sale_date) ( PARTITION p_2025_q1 VALUES LESS THAN (DATE '2025-04-01'), PARTITION p_2025_q2 VALUES LESS THAN (DATE '2025-07-01'), PARTITION p_max VALUES LESS THAN (MAXVALUE) );
要创建全局唯一索引确保 sale_id 全局唯一,且支持按 sale_date 高效分区裁剪(例如查询某时间段内所有销售单):
- ✅ 正确(分区键
sale_date为第一列,sale_id为第二列):CREATE UNIQUE INDEX idx_sales_global ON sales(sale_date, sale_id) GLOBAL PARTITION BY RANGE(sale_date) (PARTITION p_idx_q1 VALUES LESS THAN (DATE '2025-04-01'), PARTITION p_idx_q2 VALUES LESS THAN (DATE '2025-07-01'), PARTITION p_idx_max VALUES LESS THAN (MAXVALUE)); - ❌ 错误(
sale_id在前,sale_date在后):CREATE UNIQUE INDEX ... ON sales(sale_id, sale_date) GLOBAL PARTITION BY RANGE(sale_date) ...→ORA-14038 - ⚠️ 注意:如果只要求
sale_id唯一,但不关心按日期裁剪,可建非分区全局唯一索引:CREATE UNIQUE INDEX idx_sales_id_only ON sales(sale_id) GLOBAL;(不带PARTITION BY)
本地索引 vs 全局索引:唯一性约束的实际取舍
如果你的真实需求是「用唯一索引支撑主键或唯一约束」,请优先考虑 LOCAL 索引 + 包含分区键的组合:
-
CREATE UNIQUE INDEX idx_local_pk ON sales(sale_id, sale_date) LOCAL;→ 允许,且能支持ALTER TABLE ... ADD CONSTRAINT ... PRIMARY KEY USING INDEX idx_local_pk - 但注意:该约束的唯一性只在每个分区内部有效,
sale_id在不同分区重复不会报错 —— 这通常不符合主键语义 - 所以真正需要全局唯一语义(如主键),就必须用
GLOBAL UNIQUE索引,且严格满足前缀要求
性能影响:全局唯一索引在高并发 INSERT 场景下容易成为热点(所有分区都往同一索引分区写),尤其当分区键值集中(如大量插入同一天数据)时,可能引发索引块争用。
验证是否创建成功及关键视图查询
创建后务必确认索引类型、分区方式和唯一性状态:
- 查索引基本信息:
SELECT index_name, index_type, uniqueness, partitioned FROM user_indexes WHERE index_name = 'IDX_SALES_GLOBAL';→ 应返回UNIQUENESS = 'UNIQUE',PARTITIONED = 'YES' - 查是否为全局前缀索引:
SELECT index_name, partitioning_type, subpartitioning_type, status FROM user_part_indexes WHERE index_name = 'IDX_SALES_GLOBAL';→PARTITIONING_TYPE应为RANGE,且无ORA-14038即说明前缀合规 - 查索引分区详情:
SELECT partition_name, high_value FROM user_ind_partitions WHERE index_name = 'IDX_SALES_GLOBAL' ORDER BY partition_position;
最容易被忽略的一点:全局分区索引的 HIGH_VALUE 必须与表分区的 HIGH_VALUE 逻辑对齐(比如都用 DATE '2025-04-01'),否则分区裁剪失效或维护异常 —— 这不是语法强制,但属于生产环境必须人工核对的隐性契约。


















