故障现象
在 Oracle 数据库日常维护过程中,遇到一个比较特殊的问题:删除一张业务表时,Oracle 返回 ORA-14415,提示索引处于部分删除状态,要求先执行 DROP INDEX。
执行的 SQL 如下:
DROP TABLE app_user.test_table;
报错信息:
ORA-14415: index in partially dropped state, submit DROP INDEX
查询表上索引
-- 表上的 indexes
SELECT owner,
index_name,
index_type,
table_owner,
table_name,
status
FROM dba_indexes
WHERE table_owner = 'APP_USER'
AND table_name = 'TEST_TABLE'
ORDER BY index_name;
-- 表上列附属的lob indexes
SELECT i.owner,
i.index_name,
i.index_type,
i.table_owner,
i.table_name,
i.status,
i.domidx_status
FROM dba_indexes i
WHERE i.table_owner = 'xxxx'
AND i.table_name = 'xxxxx'
ORDER BY i.index_name;
-- base table ind$
create table ind$ /* index table */
( obj# number not null, /* object number */
/* DO NOT CREATE INDEX ON DATAOBJ# AS IT WILL BE UPDATED IN A SPACE
* TRANSACTION DURING TRUNCATE */
dataobj# number, /* data layer object number */
ts# number not null, /* tablespace number */
file# number not null, /* segment header file number */
block# number not null, /* segment header block number */
bo# number not null, /* object number of base table */
indmethod# number not null, /* object # for cooperative index method */
cols number not null, /* number of columns */
pctfree$ number not null, /* minimum free space percentage in a block */
initrans number not null, /* initial number of transaction */
maxtrans number not null, /* maximum number of transaction */
pctthres$ number, /* iot overflow threshold, null if not iot */
type# number not null, /* what kind of index is this? */
/* normal : 1 */
/* bitmap : 2 */
/* cluster : 3 */
/* iot - top : 4 */
/* iot - nested : 5 */
/* secondary : 6 */
/* ansi : 7 */
/* lob : 8 */
/* cooperative index method : 9 */
flags number not null,
/* mutable flags: anything permanent should go into property */
/* unusable (dls) : 0x01 */
/* analyzed : 0x02 */
/* no logging : 0x04 */
/* index is currently being built : 0x08 */
/* index creation was incomplete : 0x10 */
/* key compression enabled : 0x20 */
/* user-specified stats : 0x40 */
/* secondary index on IOT : 0x80 */
/* index is being online built : 0x100 */
/* index is being online rebuilt : 0x200 */
/* index is disabled : 0x400 */
/* global stats : 0x800 */
select * from ind$ where bo$=xxx -- table object id
这个案例发现表上的索引已不存在,但是表的lob列上还有索引。
尝试直接删除问题列
随后尝试删除表中的 LOB 相关列:
ALTER TABLE app_user.test_table
DROP COLUMN lob_col1;
仍然返回 ORA-14415。
这表明常规的列删除操作同样受到了异常索引状态的影响。
将相关列标记为 UNUSED
在确认相关列不再需要、且业务允许清理后,尝试使用 SET UNUSED COLUMN。
ALTER TABLE app_user.test_table
SET UNUSED COLUMN lob_col1;
ALTER TABLE app_user.test_table
SET UNUSED COLUMN lob_col2;
ALTER TABLE app_user.test_table
SET UNUSED COLUMN lob_col3;
执行结果均为:
Table altered.
与直接执行 DROP COLUMN 不同,SET UNUSED COLUMN 主要是将列标记为不可再使用,而不是立即完成列数据的物理清理。
在本案例中,这些操作可以成功执行。
删除 UNUSED 列
随后执行:
ALTER TABLE app_user.test_table
DROP UNUSED COLUMNS;
执行成功:
Table altered.
完成这一步后,之前标记为 UNUSED 的列被进一步清理,表的状态也发生了变化。
需要强调的是,DROP UNUSED COLUMNS 并不是一个通用的索引修复命令。本案例中它能够成功,是实际验证得到的处理结果,不能据此推断所有 ORA-14415 都能采用相同方法解决。
再次删除表
完成上述处理后,再次执行:
DROP TABLE app_user.test_table;
本案例中,表最终可以正常删除。至此,故障处理完成。
总结
当drop table 遇到ora-14415, 检查表上的索引与lob列上的索引,drop column无法绕过。set unused是一种方法。
这个案例的经验是:Oracle 表级 DDL 失败时,如果错误指向处于部分删除状态的索引,应优先检查索引及其关联的 LOB 元数据,而不是简单地将问题归结为表损坏。
另外,ORA-14415 描述的是索引的部分删除状态,并不等同于已经确认的物理索引损坏。准确识别异常对象,再选择合适的清理路径,才是这类故障处理的关键。