Oracle迁移到信创数据库——从数据字典自动生成DDL的完整方案

信创迁移不是"导出再导入"那么简单。Oracle有 VARCHAR2、NUMBER、CLOB,达梦和人大金仓有自己的类型体系。逐张表手工改写DDL?几百张表写到明年。这篇记录的是一套从Oracle数据字典自动读取表结构、自动转换数据类型、自动生成目标库DDL的方案——不是书上看的,是从迁移一线下来的实战经验。


一、信创迁移不是"换个数据库"

信创迁移的目标是把Oracle换成国产数据库——达梦、人大金仓、GBase。很多人以为迁移就是 expdp 导出 → impdp 导入。

不是。因为:

  1. 数据类型不一样——Oracle的 VARCHAR2 到达梦是 VARCHARNUMBER 如果精度小于18且scale为0应该转成 INT 而不是 DECIMAL
  2. 建表语法不一样——Oracle的 SEGMENT CREATION IMMEDIATE 到了金仓根本没有这个概念
  3. 存储过程不兼容——PL/SQL和达梦的SQL语法有大量差异,不是改几个关键字就能跑的
  4. 序列、触发器、视图——这些对象的迁移语法差异更大

几百张表,手工改写DDL不现实。几十个存储过程,逐行改写更不现实。必须有一套自动化方案——从Oracle的数据字典里读取所有对象的定义,自动转换为目标库的语法。


二、第一步:从数据字典自动生成建表DDL

Oracle的数据字典 dba_tab_columns 存了所有表的列定义——列名、数据类型、长度、精度、是否可空。核心思路是遍历这个字典,拼出目标库的CREATE TABLE语句

DECLARE
  n_count NUMBER;
  n_row   NUMBER := 0;
BEGIN
  -- 遍历指定用户下的所有表
  FOR rec IN (SELECT * FROM dba_tables t
               WHERE t.OWNER = 'CORE'
               ORDER BY t.TABLE_NAME)
  LOOP
    dbms_output.put_line('CREATE TABLE ' || rec.table_name || '(');

    n_count := 0;
    n_row   := 0;
    SELECT COUNT(*) INTO n_count
      FROM dba_tab_columns t
     WHERE t.OWNER = rec.owner
       AND t.TABLE_NAME = rec.table_name;

    -- 遍历每一列,按Oracle类型转换为目标库类型
    FOR rec1 IN (SELECT * FROM dba_tab_columns t
                  WHERE t.OWNER = rec.owner
                    AND t.TABLE_NAME = rec.table_name)
    LOOP
      n_row := n_row + 1;
      dbms_output.put('    ' || rec1.column_name);

      -- 类型映射:Oracle → SQL Server/达梦/金仓
      IF rec1.data_type = 'VARCHAR2' THEN
        dbms_output.put(' VARCHAR(' || rec1.data_length || ')');
      ELSIF rec1.data_type = 'NUMBER' THEN
        IF rec1.data_precision < 18 AND rec1.data_scale = 0 THEN
          dbms_output.put(' INT');
        ELSIF rec1.data_scale > 0 THEN
          dbms_output.put(' DECIMAL(' || rec1.data_precision || ','
                          || rec1.data_scale || ')');
        ELSE
          dbms_output.put(' BIGINT');
        END IF;
      ELSIF rec1.data_type = 'DATE' THEN
        dbms_output.put(' DATETIME');
      ELSIF rec1.data_type = 'CLOB' THEN
        dbms_output.put(' TEXT');
      ELSIF rec1.data_type = 'NVARCHAR2' THEN
        dbms_output.put(' VARCHAR(' || rec1.data_length || ')');
      END IF;

      -- 非空约束
      IF rec1.nullable = 'N' THEN
        dbms_output.put(' NOT NULL');
      END IF;

      IF n_row < n_count THEN
        dbms_output.put_line(',');
      END IF;
    END LOOP;

    dbms_output.put_line(');');

    -- 提取主键约束
    FOR rec2 IN (SELECT * FROM sys.user_constraints t
                  WHERE t.constraint_type = 'P'
                    AND t.owner = rec.owner
                    AND t.table_name = rec.table_name)
    LOOP
      dbms_output.put('ALTER TABLE ' || rec2.table_name
                      || ' ADD CONSTRAINT ' || rec2.constraint_name
                      || ' PRIMARY KEY (');
      -- 提取主键列...
      n_count := 0;
      SELECT COUNT(*) INTO n_count
        FROM sys.user_cons_columns
       WHERE constraint_name = rec2.constraint_name;
      -- 逐列拼主键...
      dbms_output.put_line(');');
    END LOOP;

  END LOOP;
END;

只跑了 ACT_RE_BINDINGFORM 这一张表来验证逻辑——把生成的SQL复制到目标库执行,表结构完全正确。后面的表按同样逻辑批量生成,几百张表半小时全部出完。


三、类型映射的核心规则

Oracle和国产库的类型不是一一对应的。映射错了,数据精度丢失、日期格式乱码、主键约束建不上。

Oracle类型SQL Server达梦判断逻辑
VARCHAR2(N)VARCHAR(N)VARCHAR(N)直接映射
NUMBER(p,0) 且 p<18INTINT整数且不超精度
NUMBER(p,s) 且 s>0DECIMAL(p,s)DECIMAL(p,s)有小数位
NUMBER(p,0) 且 p>=18BIGINTBIGINT长整型
DATEDATETIMEDATETIME日期时间
CLOBTEXTTEXT大文本
NVARCHAR2VARCHARVARCHAR去掉N前缀

关键判断在 NUMBER 类型的分支逻辑——同一个 NUMBER,根据 DATA_PRECISIONDATA_SCALE 的不同组合,映射到三种完全不同的目标类型。这是手写DDL最容易出错的地方——少了一个判定分支,INT变成了DECIMAL,精度就丢了。


四、不只是建表——其他对象的迁移

表结构只是迁移的第一步。完整的数据库迁移还包括:

索引:从 dba_indexesdba_ind_columns 读取索引定义,生成对应的 CREATE INDEX 语句。注意Oracle的位图索引到达梦可能不支持,需要改写成B-Tree索引。

序列CREATE SEQUENCE 在Oracle、达梦、金仓之间的语法基本一致,但 NOCACHE / CACHE 的定义有差异。从 dba_sequences 读取当前值,迁移后需要把序列的当前值设回去——否则新插入数据的主键会冲突。

视图:从 dba_views 读取视图定义的TEXT字段。但Oracle的视图里可能有 DECODECONNECT BY 这些Oracle独有的函数和语法——直接复制到目标库大概率报错。需要逐条检查,替换成目标库等价的函数。

存储过程:这是最难的部分。Oracle的PL/SQL和达梦的SQL在游标声明、异常处理、BULK COLLECT 等方面语法差异巨大。没有自动化工具能做完整的PL/SQL→目标库语法转换——只能逐行改写。


五、迁移策略:分步走,每步验证

第一步:导出Oracle表结构 → 自动生成目标库DDL
   └── 验证:在目标库执行DDL,确认所有表建成且无语法错误

第二步:导出索引、序列、视图 → 自动生成目标库DDL
   └── 验证:索引是否全建、序列从当前值开始、视图能编译通过

第三步:数据迁移(DBLink直抽或导出导入)
   └── 验证:行数比对、主键唯一性检查、日期字段无乱码

第四步:存储过程——人工逐行改写
   └── 验证:单元测试覆盖核心业务逻辑

第五步:应用层改造——JDBC驱动替换、SQL方言适配
   └── 验证:全链路回归测试

不能在第一步还没验证完的时候就跳到第三步——如果表的字段类型映射错了,导入了数据之后才发现,已经晚了。

关键原则:每个阶段都做一次验证再进入下一阶段。 不是一口气全干完然后祈祷没有问题。


六、亮点总结

✅ 数据字典驱动——从 dba_tab_columns 自动读取几百张表的结构,不手工改一行DDL
✅ 类型映射完备——NUMBER按精度和scale三分支映射,区分INT/DECIMAL/BIGINT
✅ 约束自动迁移——主键、非空从数据字典自动生成,不会漏
✅ 分步验证——建表→索引→序列→数据→存储过程,每步验证后再进行下一步
✅ 可反向操作——同样的逻辑反过来也可以把目标库的表结构导出为Oracle DDL

七、适用场景

  • Oracle到达梦/人大金仓/GBase的信创迁移
  • 任何需要"从数据字典反推DDL"的场景——不仅仅是信创,数据库版本升级、跨平台迁移都适用
  • 几百张表需要批量生成建表语句,手工改写不现实的项目

八、扩展方向

  1. 完整DDL生成——增加索引、序列、视图、触发器的自动生成逻辑
  2. PL/SQL语法自动检查——扫描存储过程中Oracle独有的语法,标记需要人工改写的部分
  3. 迁移报告——对比源库和目标库的表结构差异,生成迁移对照清单

九、结语

信创迁移最大的工作量不是"数据怎么搬过去"——expdp/impdp 现成的工具多的是。最大头是表结构、索引、视图、存储过程的语法改写——几百张表的DDL要逐行改写成目标库的语法。

从数据字典里批量读出来、自动生成DDL——这个思路本身就是信创迁移里的核心经验。不是"会写SQL"能搞定的事,是"知道从哪取元数据、怎么映射类型、怎么分步验证"的系统性认知。

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐