訴求mysql
SDEsql
OBJECTID數據庫
ArcMap編輯緩存
重置OBJECTIDbash
非GIS專業的人員可能很難理解ArcSDE中的表OBJECTID的重要性,要麼總想着本身動手去維護,要麼就想直接忽略它,致使總會出現OBJECTID的衝突,編輯數據報錯oracle
下面簡單談談對OBJECTID的粗淺認識,拋磚引玉,幫助更多人理解它的內部機制app
ArcGIS家的ArcSDE空間數據庫引擎,官方定義去官網,那裏有不少,但我的感受像機翻的,讀起來很拗口。SDE就是在數據庫中添加一箇中間件,這樣能夠存儲和管理空間數據,雖然不少數據庫像oracle,mysql,sqlserver等都有自家的管理空間數據策略,但和ArcGIS互通方面差一些,想利用ArcGIS強大的生態,因此會考慮使用ArcSDE中間件,若是是ArcGIS的老手,其實對此也無所謂。空間數據庫的本質就是定義了一套操做和管理空間數據的數據模型,就像文本數據類型,數據庫定義了對它的分割、合併、拼接、轉換等。只是空間數據會複雜些,空間關係運算存儲等要費事的多。函數
表面上的體現就是安裝了ArcSDE中間件後,會有一個SDE用戶,這個用戶下就包含對空間數據模型的定義、維護管理,裏面有不少管理表、註冊表、工具函數等。工具
ArcGIS管理的數據表中OBJECTID是一個惟一主鍵標識,而且與一個序列Sequence對應,(能夠理解序列就是一個由初始值,步長,最大值的遞增函數),每新增一條數據,序列就會按步長遞增生成序號。每每不須要你手動干預,除非你沒按人家規則,人爲破壞生成機制。sqlserver
好比有時候咱們須要經過SQL插入一條數據,那麼OBJECTID是非空,你須要從序列中獲取。表的OBJECTID和序列的對應能夠在SDE用戶下的TABLE_REGISTRY找到,像下面這樣
select t.registration_id from SDE.TABLE_REGISTRY t where Owner='yourowner' and Table_Name='yourtablename';
複製代碼
或者直接調用sde.gdb_util.next_rowid,它會調用序列獲得nextValue的。
但事情每每沒有那麼簡單
咱們在用ArcMap編輯SDE中空間數據時,像新增,若是直接從序列中獲取,那多用戶下就會有鎖定。
咱們看下sde.gdb_util.next_rowid方法的源碼
owner_l := UPPER(owner_in);
L_table_name(owner_in,table_in,table_l);
p_name := UPPER(owner_in||'.'||table_l);
stmt := 'SELECT registration_id FROM SDE.table_registry '||
'WHERE owner = :owner_l AND table_name = :table_l';
OPEN C_gdb FOR stmt USING owner_l,table_l;
FETCH C_gdb INTO reg_id;
IF C_gdb%NOTFOUND THEN
raise_application_error(SDE.sde_util.SE_TABLE_NOREGISTERED,
'Class '||p_name||' not registered to the Geodatabase.');
End If;
CLOSE C_gdb;
s_value := SDE.version_user_ddl.next_row_id (owner_l,reg_id);
return(s_value);
複製代碼
SDE.version_user_ddl.next_row_id的主要源碼片斷以下,感興趣的能夠研究下
BEGIN
-- See if there are any ids for this table in its pipe.
pipe_name := 'ArcSDE_IdPipe' || TO_CHAR (registration_id);
pipe_result := DBMS_PIPE.RECEIVE_MESSAGE (pipe_name,0);
IF pipe_result = 0 THEN
-- Found ids in the pipe, read them.
DBMS_PIPE.UNPACK_MESSAGE (id_start);
DBMS_PIPE.UNPACK_MESSAGE (id_count);
next_id := id_start;
id_start := id_start + 1;
id_count := id_count - 1;
ELSE
-- Fetch ids from the sequence. Also get the sequence's -- increment by value so we know how many ids we actually got. sequence_name := 'R' || TO_CHAR (registration_id); select_statement := 'SELECT ' || TO_CHAR(owner) || '.' || sequence_name || '.NEXTVAL FROM DUAL'; BEGIN EXECUTE IMMEDIATE select_statement INTO next_id; EXCEPTION WHEN OTHERS THEN raise_application_error (sde_util.SE_NO_SDE_ROWID_COLUMN, 'Unable to find or access sequence ' || owner || '.r' || TO_CHAR (registration_id)); END; OPEN increment_by_cursor (owner,sequence_name); FETCH increment_by_cursor INTO increment_by; CLOSE increment_by_cursor; id_start := next_id + 1; id_count := increment_by - 1; END IF; -- Write any remaining ids back into the pipe. IF id_count > 0 THEN DBMS_PIPE.RESET_BUFFER; DBMS_PIPE.PACK_MESSAGE (id_start); DBMS_PIPE.PACK_MESSAGE (id_count); pipe_result := DBMS_PIPE.SEND_MESSAGE (pipe_name,4); END IF; -- Return the id we found. RETURN next_id; END next_row_id; 複製代碼
上面主要作了兩件事情,首先咱們sde.gdb_util.next_rowid時不是想象中的從R序列中直接獲取,而是先去一個特定管道ArcSDE_IdPipe+表序列名中拿,沒有再從R序列中獲取,並一次取步長的範圍。因此有時候會事與願違的發現獲得的nextValue和想象中的不同。當管道中沒有值時纔去序列獲取。有時候OBJECTID已經錯亂(新增報錯OBJECTID重複),咱們只調整序列的當前值不足以解決問題,管道中獲取的值可能仍是小於序列值的,可能致使後面仍是重複。因此建議更新時,先清空管道,讓第一次讀取就從序列中讀取。
管道用於多會話之間傳遞信息。
若是OBJECTID已經錯亂,若是正確重置?咱們甚至遇到過,當查看oracle中OBJECTID類型是INT,INT最大數才2147483648,實際裏面居然存儲卻遠大於這個數。查閱oracle文檔會發現,底層oracle存儲int是映射到number的,因此範圍會大不少。但這也給咱們提了醒,OBJECTID天然增加怎麼會增加到遠超2147483648,咱們通常不會在一個表裏放幾十億,幾百億的數據量吧。這可能就是亂用或忽略OBJECTID生成策略帶來的小坑。
基於以上的解讀,重置OBJECTID的基本流程應該是:清空管道,更新OBJECTID,更新序列。在oracle中寫了一個存儲過程以供參考,源碼以下:
年紀大了,記性很差,記錄一下,方便查閱
剛說什麼來着?emmmmmm,對,源碼
create or replace procedure P_ResetObjectID(owner in NVARCHAR2, tablename in NVARCHAR2)
is
/*
重置sde table objectID
*/
reg_id pls_integer;
owner_l VARCHAR2(64);
table_l VARCHAR2(64);
pipe_name VARCHAR2(30);
max_id NUMBER;
stmt varchar2(512);
sequence_name VARCHAR2(64);
begin
owner_l := UPPER(owner);
table_l := UPPER(tablename);
stmt := 'SELECT registration_id FROM SDE.table_registry '||
'WHERE owner ='''|| owner_l ||''' AND table_name = '''|| table_l ||'''';
EXECUTE IMMEDIATE stmt INTO reg_id;
if reg_id is null then return;
end if;
pipe_name := 'ArcSDE_IdPipe' || TO_CHAR (reg_id);
--清除arcgis緩存管道
stmt := 'select sys.dbms_pipe.remove_pipe('''||pipe_name||''') from dual ';
EXECUTE IMMEDIATE stmt;
--更新oid
stmt := 'update '||table_l||' set objectid=rownum ';
EXECUTE IMMEDIATE stmt;
--獲取最大oid
stmt := 'select max(objectid) from '||table_l;
EXECUTE IMMEDIATE stmt INTO max_id;
if max_id is NULL then return;
end if;
--修改序列的當前值
sequence_name := 'R' || TO_CHAR (reg_id);
stmt := 'drop sequence '||sequence_name||' ';
EXECUTE IMMEDIATE stmt;
stmt := 'create sequence '||sequence_name||' minvalue 1 maxvalue 2147483647 start with '||max_id||' increment by 16 cache 20';
EXECUTE IMMEDIATE stmt;
stmt := 'grant select on '||owner_l||'.'||sequence_name||' to public';
EXECUTE IMMEDIATE stmt;
end P_ResetObjectID;
複製代碼
手機在手邊,可關注vx:xishaobb,互動或獲取更多信息,歡迎交流指正,固然這裏也一直更新de,有緣見,拜了個拜拜