Oracle && OceanBase 相关文档,希望互相学习,
共同进步
1.背景
Oracle 19c,今天准备清理一下之前跑的部分报表的数据,由于表中数据量比较大,大约680万数据,待清理 59万,于是分批按条件进行删除。但是执行一部分后,报错如下:
ORA-08102: 未找到索引关键字,对象号1955591,文件84,块1571386

本文主要针对大批量数据清理、索引异常排查 及问题解决,进行日志记录。
2. 实验
2.1 相关表及存储过程
需求:删除sun_public_info 表中,字段为 enum_name 的指定范围内的数据。同步删除关联的子表的数据。由于数据量比较大,编写包函数进行循环进行删除。
1)涉及表
需求:删除sun_public_info 表中,enum_name 在指定范围的数据。

2) 涉及处理过程
作用:将表中数据,循环删除
sql
create or replace package PG_SUN_UPDATE is
/*--------------------------
创建时间 : 2026.09.15
创建人 : FENGLY
说明 : TEST
---------------------------*/
function TEST2 (pDataIssue in varchar2) return varchar2;
end PG_SUN_UPDATE ;
/
CREATE OR REPLACE PACKAGE BODY PG_SUN_UPDATE is
/*--------------------------
创建时间 : 2026.09.15
创建人 : FENGLY
说明 : TEST
---------------------------*/
function TEST2 (pDataIssue in varchar2) return varchar2 is
vResult varchar2(10);
vFunctionName varchar2(1000);
vTableName varchar2(1000);
vEnumName varchar2(100);
vMsg varchar2(1000);
vCount1 number;
vCount2 number;
vSQL varchar2(3000);
vSUN_PUBLIC_DATA varchar2(100);
begin
vResult := '0';
vFunctionName := 'PG_SUN_UPDATE.TEST2 -上线前清空';
vTableName := 'sun_public_info -check ';
BEGIN
GET_LOGGER(pDataIssue, vFunctionName, vTableName, '0', '开始跑批');
execute immediate 'alter table SUN_PUBLIC_INFO nologging';
FOR vCursor IN ( SELECT T.KID,T.TABLE_NAME,T.ENUM_NAME,T.TITLE,T.KEY_CONTITION,
T.SUN_TABLE_NAME,
DECODE(T.TABLE_NAME ,'OPEN_FM_RPT_SR','OPEN_SUN_PUBLIC_DATA_SR'
,'OPEN_FM_RPT_ZC','OPEN_SUN_PUBLIC_DATA_ZC'
,T.TABLE_NAME) OPEN_TABLE_NAME
FROM OUT_OPEN_PARAM_TABLES@out T
WHERE T.DELETED='1' --新版自动跑批的17个表
AND T.ENUM_NAME NOT IN('ORG_STRUCTURE_PUBLIC','FOCUS_OPEN')
ORDER BY T.KID )
LOOP
vEnumName := vCursor.ENUM_NAME ;
vSUN_PUBLIC_DATA := vCursor.SUN_TABLE_NAME;
--1)info表条数
SELECT /*+PARALLEL(5)*/COUNT(*) into vCount1
FROM SUN_PUBLIC_INFO T
WHERE T.ENUM_NAME = vEnumName ;
--2)data表条数
vSQL := 'SELECT /*+PARALLEL(5)*/COUNT(*) FROM '||vSUN_PUBLIC_DATA;
EXECUTE IMMEDIATE vSQL into vCount2;
GET_LOGGER(pDataIssue, vFunctionName, vSUN_PUBLIC_DATA, '0', vEnumName||' info表:'||vCount1||' 条,data表:'||vCount2||' 条');
--3)上线前清空
execute immediate 'truncate table '||vSUN_PUBLIC_DATA;
IF vCount1 > 0 then
DELETE /*+PARALLEL(5)*/
FROM SUN_PUBLIC_INFO T
WHERE T.ENUM_NAME = vEnumName ;
COMMIT;
END IF;
GET_LOGGER(pDataIssue, vFunctionName, vTableName||'-'||vSUN_PUBLIC_DATA, '0', vEnumName||' 清空结束');
END LOOP;
execute immediate 'alter table SUN_PUBLIC_INFO logging';
GET_LOGGER(pDataIssue, vFunctionName, vTableName, '0', '结束跑批');
EXCEPTION
WHEN OTHERS THEN
vMsg := '报错位置:' || dbms_utility.format_error_backtrace || '报错信息:' ||
substr(SQLERRM, 1, 200);
GET_LOGGER(pDataIssue, vFunctionName, vEnumName, '3', vMsg);
vResult := '1';
END;
return vResult;
end;
end PG_SUN_UPDATE ;
/
2.2 报错浮现

报错位置:ORA-06512: 在 "xx.PG_SUN_UPDATE", line 496
ORA-06512: 在 "NH_MCRO_AFFAIRS.PG_SUN_UPDATE", line 496
报错信息:ORA-08102: 未找到索引关键字, 对象号 1955591, 文件 84, 块 1571386 (2)
看到表是数据量比较大,表做的日期分区,处理时用的 非分区字段的索引 enum_name,进行检索后删除,删除时报错,执行语句如下:
sql
DELETE from SUN_PUBLIC_INFO T WHERE T.ENUM_NAME='FM_AS_CDQTZ'
把该语句从处理函数包中拿出,单独plsql执行,依然报错相同,问题浮现。

ORA-08102: 未找到索引关键字, 对象号 1955591, 文件 84, 块 1571386 (2)
一般是索引与表数据出现了逻辑不一致:索引中存储的键值条目和数据表中的实际数据不匹配,执行删除操作时Oracle尝试同步更新索引条目,却找不到对应的索引键,直接抛出异常终止操作。
错误类型:Oracle数据库内部索引一致性异常
核心指向 :索引中存储的键值与对应数据表中的实际数据不匹配,导致数据库访问索引时无法找到对应关键字
关联定位:通过对象号1955591可以在数据库中精准定位到发生异常的索引对象,文件84和块1571386标记了异常索引所在的物理存储位置
常见触发原因:数据库运行过程中遭遇意外断电、硬件IO错误,导致索引数据块写入不完整
Oracle数据库自身的已知Bug引发索引与表数据不同步
数据迁移、异常强制拉起数据库等非常规操作,破坏了索引的完整性
🛠️ 常规处理方案
- 定位异常对象 :执行查询语句
select owner, object_name, object_type from dba_objects where object_id = 1955591;, 确认出问题的索引所属的用户、名称和类型。- 在线重建索引 :优先使用
alter index 索引名称 rebuild online;命令重建索引,该方式会直接读取表的完整数据重新生成索引,避免沿用原有损坏的索引数据。- 极端修复方式:如果重建无效,可以先删除该索引再重新创建
2.3 报错处理
1)定位对象
报错中,看到提示是索引未找到,查询对象号:
sqlselect owner, object_name, object_type from dba_objects where object_id = 1955591;执行截图:发现确实是处理表的索引
联想到业务前端时间确实这个字段类型有变过,有可能索引失效,想着要么在线重建索引、或者删掉重建索引。
2)在线重建索引
sqlalter index SUN_PUBLIC_INFO_AS_OF_EOM_DT_index rebuild online;执行后,却提示 索引不存在。感觉挺奇怪的,查出来确实是本用户下的索引。于是,想着要么删了重建。
3)删掉重建索引
sql
DROP INDEX SUN_PUBLIC_INFO_AS_OF_EOM_DT_index;
执行后,依然却提示 索引不存在。😅😅尴尬,还是不存在,明明dba_object 查到是它,却报不存在。小伙伴们,到这里你猜到什么原因了吗?
仔细看,
看到的索引列表里,SUN_PUBLIC_INFO_AS_OF_EOM_DT_index等索引明明在界面中显示存在,执行删除操作却提示不存在。 再仔细看,里面有个小写字母。而Oracle默认会把未加双引号的索引名自动转为大写,而这个索引时名称带小写后缀,直接写drop index SUN_PUBLIC_INFO_AS_OF_EOM_DT_index会被自动转成大写,自然找不到匹配的索引。
于是重新执行:
sqlalter index "SUN_PUBLIC_INFO_AS_OF_EOM_DT_index" rebuild online; 或 DROP INDEX "SUN_PUBLIC_INFO_AS_OF_EOM_DT_index"发现都执行正常了。原因就是创建索引时不规范,采用了部分字符小写导致的。
再重新执行之前报错的语句,OK

此时,问题解决。
2.4 检查清理结果
sql
SELECT table_name, num_rows FROM user_tables
WHERE TABLE_NAME ='SUN_PUBLIC_INFO'
ORDER BY table_name;
;
SELECT COUNT(*) FROM SUN_PUBLIC_INFO;
结果截图:

发现,表中数据不一致。需要重新收集统计信息,才能让 num_rows 变为 0 或最新值。
sql
--手动收集统计信息
BEGIN
DBMS_STATS.GATHER_TABLE_STATS(
ownname => 'xx', -- 当前用户模式名
tabname => 'SUN_PUBLIC_INFO', -- 替换为具体的表名,如 'RPT_SALES'
cascade => TRUE -- 同时更新索引统计信息
);
END;
/
再查,两表结果一致。

如果多个表,批量执行如下:
sql
--使用匿名块批量处理符合条件的表
BEGIN
FOR t IN (SELECT table_name FROM user_tables WHERE table_name LIKE '%RPT%') LOOP
BEGIN
DBMS_STATS.GATHER_TABLE_STATS(
ownname => USER,
tabname => t.table_name,
cascade => TRUE
);
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('Failed to gather stats for: ' || t.table_name);
END;
END LOOP;
END;
/
注意:
- 不要依赖
num_rows判断实时行数 :它仅用于优化器生成执行计划参考。如果需要知道确切行数,必须使用SELECT COUNT(*)。- Truncate 后建议立即收集统计:尤其是对于大数据量的表,truncate 后如果不更新统计,优化器可能仍认为该表有大量数据,从而选择错误的执行计划(如不走索引或选择错误的连接方式)。
- 自动统计任务:Oracle 默认有自动统计收集任务(通常在夜间维护窗口运行),但如果你的业务对统计信息时效性要求高,建议在大批量数据变更(如 Truncate、大量 Insert)后手动触发收集。
3.总结
1)索引异常后,可以通过重建索引解决。注意索引名大小写。
2)select count(*) 和 num_rows 的区别
| 特性 | SELECT COUNT(*) |
user_tables.num_rows |
|---|---|---|
| 数据来源 | 实时扫描全表(或索引快速计数) | 数据字典中的统计快照 |
| 准确性 | 绝对准确 | 可能过时(依赖上次收集时间) |
| 性能 | 大表慢,消耗 I/O | 极快,直接查字典 |
| 更新时机 | 每次查询都计算 | 仅在运行 DBMS_STATS 或自动任务时更新 |
| DDL 影响 | 反映最新状态 | 不受 DDL(如 Truncate)自动影响 |
实验验证:ok 
项目管理--相关知识 
项目管理-项目绩效域1/2_八大绩效域和十大管理有什么联系-CSDN博客
项目管理-计算题公式【复习】_项目管理进度计算题公式:乐观-CSDN博客
项目管理-相关知识(组织通用治理、组织通用管理、法律法规与标准规范)-CSDN博客
Oracle其他文档,希望互相学习,
共同进步
Oracle-找回误删的表数据(LogMiner 挖掘日志)_oracle日志挖掘恢复数据-CSDN博客
oracle 跟踪文件--审计日志_oracle审计日志-CSDN博客
ORA-12899报错,遇到数据表某字段长度奇怪现象:"Oracle字符型,长度50"但length查却没有50_varchar(50) oracle 超出截断-CSDN博客
