• 关于ArcGIS的OBJECTID生成策略拙见


    目录

    诉求SDEOBJECTIDArcMap编辑重置OBJECTID

    诉求

    非GIS专业的人员可能很难理解ArcSDE中的表OBJECTID的重要性,要么总想着自己动手去维护,要么就想直接忽略它,导致总会出现OBJECTID的冲突,编辑数据报错

    下面简单谈谈对OBJECTID的粗浅认识,抛砖引玉,帮助更多人理解它的内部机制

    SDE

    ArcGIS家的ArcSDE空间数据库引擎,官方定义去官网,那里有很多,但个人感觉像机翻的,读起来很拗口。SDE就是在数据库中添加一个中间件,这样可以存储和管理空间数据,虽然很多数据库像oracle,mysql,sqlserver等都有自家的管理空间数据策略,但和ArcGIS互通方面差一些,想利用ArcGIS强大的生态,所以会考虑使用ArcSDE中间件,如果是ArcGIS的老手,其实对此也无所谓。空间数据库的本质就是定义了一套操作和管理空间数据的数据模型,就像文本数据类型,数据库定义了对它的分割、合并、拼接、转换等。只是空间数据会复杂些,空间关系运算存储等要费事的多。

    表面上的体现就是安装了ArcSDE中间件后,会有一个SDE用户,这个用户下就包含对空间数据模型的定义、维护管理,里面有很多管理表、注册表、工具函数等。

    OBJECTID

    ArcGIS管理的数据表中OBJECTID是一个唯一主键标识,并且与一个序列Sequence对应,(可以理解序列就是一个由初始值,步长,最大值的递增函数),每新增一条数据,序列就会按步长递增生成序号。往往不需要你手动干预,除非你没按人家规则,人为破坏生成机制。

    比如有时候我们需要通过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编辑

    我们在用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

    如果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,有缘见,拜了个拜拜

  • 相关阅读:
    文件上传漏洞全面渗透姿势总结
    注册frpc为windows服务,可在未登录用户时启动
    SpringIOC 容器注入方式
    如何交换 Integer 的值?
    分布式websocket服务器
    win10安装Hbase2.3.0
    hadoop常用的命令
    win10安装kafka 2.122.8.1
    win10激活码
    win10 flume source为spooldir,输出到hdfs
  • 原文地址:https://www.cnblogs.com/xibei/p/11799600.html
Copyright © 2020-2023  润新知