ArcMap导入GDB数据至SDE报错ORA-00955名称已由现有对象使用解决方法
从一条弹窗开始
同事使用 ArcMap 10.0 从 GDB 往 SDE 里粘贴数据,弹了个窗:

粘贴 CZRQ 失败
Underlying DBMS error [Error executing PL/SQL Block
db_stgeom_create_index::ORA-29855:执行 ODCIINDEXCREATE 例行程序时出错
ORA-00955: 名称已由现有对象使用]
ORA-00955,名字被占了。谁占的?占的哪个名字?弹窗里一个字都没写。剩下的事情就是一层层把它挖出来。
先交代环境:ArcSDE 10.0(Build 685)+ Oracle 10.2.0.3,单机部署,SDE 是空间库 owner。CZRQ 数据集里有十几个图层,物理表名是 AGAS 前缀(比如 AGASFAMEN、AGASTIAOYAXIANG)。顺便说一句,这个坑和数据集内容没关系,换个数据集一样踩。
第一层:日志里发生了什么
ArcSDE 的日志(sde_esri_sde.log)能看到这次粘贴的轨迹:12:05 到 12:06 之间,一批图层表陆续建了出来,每个表一个 R9xx_sde_rowid_uk 唯一索引,一路建到 R950。然后,日志突然断在那里,再也没有动静。
几个小时后出现 ORA-03114 未连接到 ORACLE,giomgr 重新初始化。也就是说,导入是在建索引的当口掉线的,掉线前已经建了半批对象。这类半成品是后面一切问题的起点。
第二层:库里留下了一个孤儿 S0_IDX$
查最近三天新建的对象:
SELECT owner, object_name, object_type, created
FROM dba_objects
WHERE created > SYSDATE - 3
ORDER BY created;
结果里绝大多数是回收站的 BIN$ 对象(表被回滚删掉了),但有三个活的:
S0_IDX$(TABLE)S0$_IX1、S0$_IX2(INDEX,挂在 S0_IDX$ 上)
它们挂着 S0 前缀,明摆着是 ST_Geometry 的东西。ST_Geometry 在 Oracle 里有个坑:官方文档写得很清楚,空间索引除了本身的域索引,还会建一张附属的 S 表,命名规则和 SDE.ST_GEOMETRY_INDEX 里的 INDEX_ID 挂钩,比如 INDEX_ID 为 16 的索引,辅助表就叫 S16_IDX$。
也就是说,S0_IDX$ 是某个 INDEX_ID=0 的空间索引留下的辅助表。创建它的那次操作没写完,表留下了,登记没留下,成了孤儿。
第三层:三方对照,孤儿只有它一个
把三张清单摆到一起对账:
SDE.ST_GEOMETRY_INDEX里所有已登记的空间索引(INDEX_ID 从 1 到 945,共 119 条)dba_tables里所有S%_IDX$辅助表dba_indexes里所有DOMAIN类型的域索引(A###_IX1系列)
对完发现:除了 S0_IDX$,其他 119 个辅助表和域索引全部对得上号。这个对账过程很值,它把所有"疑似"都排除了,只剩一个明确目标。
删掉它:
DROP TABLE SDE.S0_IDX$ PURGE;
PURGE DBA_RECYCLEBIN;
注意:PURGE DBA_RECYCLEBIN 需要DBA权限才能执行。
第四层:删了还报错,说明撞名另有其人
重新粘贴,还是同样的 ORA-955。但这次观察到一个关键细节:S0_IDX$ 在粘贴瞬间又被新建了一次(15:24:08)。也就是说,它根本不是残留物,而是每次建空间索引时 SDE 都要建的构建表。失败后没人收拾它,下一次建索引先撞上它,于是每次都报 ORA-955。
那这次失败撞的是谁?失败只留下了新建的 S0_IDX$,没有别的对象。说明撞名发生在"建完 S0_IDX$ 之后、建正式辅助表 S<INDEX_ID>_IDX$ 时"。要找到这个编号,去看序列。
第五层:真正的根因,一个失步的序列
查 SDE 名下所有序列:
SELECT sequence_name, last_number
FROM dba_sequences
WHERE sequence_owner='SDE'
ORDER BY sequence_name;
重点来了:
SEQ_ST_INDEX_ID 169
而 ST_GEOMETRY_INDEX 里的 INDEX_ID 已经用到 945。
SEQ_ST_INDEX_ID 就是分配 INDEX_ID 的序列,但它停在 169,比表里的实际最大值落后了一大截。这意味着每次新建空间索引,SDE 从序列拿到的编号都落在 169~256 这段区间——而这些编号对应的 S169_IDX$、S170_IDX$……全都已经存在了。于是建辅助表 S<INDEX_ID>_IDX$ 时必撞名,一次都跑不掉。
至于序列为什么落后,大概率是之前某次操作重建或重置过它。具体的"谁干的"已经查不到了,但修复本身很简单。
解决方法
把序列拨到超过当前最大 INDEX_ID 的位置:
ALTER SEQUENCE SDE.SEQ_ST_INDEX_ID INCREMENT BY 2000;
SELECT SDE.SEQ_ST_INDEX_ID.NEXTVAL FROM DUAL;
ALTER SEQUENCE SDE.SEQ_ST_INDEX_ID INCREMENT BY 1;
再清掉上次失败留下的构建表:
DROP TABLE SDE.S0_IDX$ PURGE;
PURGE DBA_RECYCLEBIN;
然后重新粘贴。这次成功了。
排查中顺带发现的第二个坑:ORA-28595
为了验证,我用 SQL*Plus 手工建了一张测试表插数据,结果撞上另一个报错:
ORA-28595: Extproc 代理: DLL 路径无效
ORA-06512: 在 "SDE.ST_GEOMETRY_SHAPELIB_PKG", line 12
这是 ST_Geometry 的形状库 DLL(10.0 里是 st_shapelib.dll)通过 EXTPROC 加载失败。查登记路径:
SELECT library_name, file_spec FROM dba_libraries WHERE owner='SDE';
再核对文件在不在,不对就重建库对象:
CREATE OR REPLACE LIBRARY SDE.ST_GEOMETRY_SHAPELIB AS
'C:\Program Files (x86)\ArcGIS\ArcSDE\ora10gexe\bin\st_shapelib.dll';
GRANT EXECUTE ON SDE.ST_GEOMETRY_SHAPELIB TO PUBLIC;
这个错不影响 ArcGIS 粘贴(粘贴走 SDE 服务进程,不一定经过 EXTPROC),但业务系统如果直接写 SQL 调 ST_Geometry 函数,就会受影响。有直连 SQL 用空间数据的,值得一并排查。
经验总结
- ORA-00955 这类报错查不出撞名对象时,别只看报错,去对账。 系统表、辅助表、域索引三方一对比,孤儿立刻现形。
- ST_Geometry 空间索引的命名要记住:辅助表 =
S<INDEX_ID>_IDX$,INDEX_ID由SDE.SEQ_ST_INDEX_ID分配。 这个知识点能帮你判断撞的是谁。 S0_IDX$是每次建空间索引的构建表,不是永久对象。 它反复出现不要慌,导入失败后清理掉即可,但要注意它只是"表",序列失步才是它反复作祟的原因。- 序列和表的数据不一致,是这类"名字被占用"问题的重灾区。 序列落后、重复、被重置,都会造成新对象撞旧对象。检查序列的
last_number和对应表的实际最大值,应该成为这类排查的固定动作。 - 导入中途掉线是产生半成品对象的温床。 这次问题能发酵成连锁故障,源头就是 12:06 那一次连接中断。
sqlnet.ora里加一句SQLNET.EXPIRE_TIME=10,能减少空闲连接被防火墙掐掉的情况。更重要是:导入大数据集时,挑业务空闲时段,别去动它。
排查 SQL 清单
-- 1. 最近新建的对象,找残留
SELECT owner, object_name, object_type, created
FROM dba_objects WHERE owner='SDE' AND created > SYSDATE - 1/24;
-- 2. 空间索引登记情况
SELECT * FROM SDE.ST_GEOMETRY_INDEX ORDER BY INDEX_ID;
-- 3. 辅助表清单
SELECT owner, table_name FROM dba_tables
WHERE owner='SDE' AND table_name LIKE 'S%_IDX$';
-- 4. 域索引(空间索引本体)
SELECT index_name, table_name, status FROM dba_indexes
WHERE owner='SDE' AND index_type='DOMAIN' AND index_name NOT LIKE 'BIN$%';
-- 5. 序列(重点查 SEQ_ST_INDEX_ID)
SELECT sequence_name, last_number FROM dba_sequences
WHERE sequence_owner='SDE' ORDER BY sequence_name;
-- 6. 清理
DROP TABLE SDE.S0_IDX$ PURGE;
PURGE DBA_RECYCLEBIN;
ALTER SEQUENCE SDE.SEQ_ST_INDEX_ID INCREMENT BY 2000;
SELECT SDE.SEQ_ST_INDEX_ID.NEXTVAL FROM DUAL;
ALTER SEQUENCE SDE.SEQ_ST_INDEX_ID INCREMENT BY 1;