Oracle 迁移达梦避坑:初始化参数、SQL 语法与驱动适配
按初始化参数、SQL 语法差异、驱动与 ORM 三层梳理 Oracle 迁移达梦与金仓的兼容性避坑清单
从 Oracle 迁移到达梦后出现分页错乱、空串行为变化、中文被截断、存储过程编译失败,多数并不是"达梦不兼容 Oracle",而是三类差异叠加的结果:初始化参数(多数建库时定死、事后难改)、SQL 语法与语义差异、驱动与 ORM 适配。最有效的避坑方式就是按这三层组织检查清单,从下往上逐层验证——参数层错了返工最重(页大小、字符集这类只能重建实例,兼容模式可改但代价高),语法层错了改 SQL,驱动层错了改应用配置,三层的返工成本完全不同。达梦官方的 Oracle 迁移文档也是按兼容性差异组织的,可以作为逐项核对的底稿。
第一层:初始化参数——建库时定死的与改起来代价高的
这一层是风险最大的一层。页大小、字符集、大小写敏感在初始化实例时确定,上线后再发现不合适,往往只能重建数据库、重灌数据;兼容模式则不同,它写在 dm.ini 里,技术上可以修改、重启生效,但修改会影响数据存储与操作结果,代价高、须慎重,所以同样建议建库时定死并在迁移全程固定。
- 页大小(PAGE_SIZE):常见取值 4K、8K、16K、32K,初始化后不可更改。它决定单行数据长度上限与索引组织方式,迁移前要按 Oracle 库里的最大行宽(宽表、大字段、多列 VARCHAR)估算,宁可放大一档。
- 字符集:GB18030 与 UTF-8 的选择在建库时确定,直接决定中文字符的存储字节数、排序比较规则,以及"看起来没超长却报超长或被截断"的行为差异。迁移前先确认 Oracle 库的字符集与应用写入编码,再反推目标库字符集。
- 大小写敏感(CASE_SENSITIVE):建库时确定。Oracle 默认标识符不敏感、字符串比较区分大小写,两边约定不一致时,迁移后会出现"同一条 SQL 查不到数据"这类很难往参数层联想的问题。
- 兼容模式(COMPATIBLE_MODE):dm.ini 里的兼容参数,推荐取值 2 以兼容 Oracle,影响空值处理、语法支持与函数行为。它与上面三项不同,不属于"初始化后改不动":在 dm.ini 中调整后重启实例即可生效,但修改会影响数据存储与操作结果,官方也提示修改须慎重,中途切换还会让前后行为不一致、比对结果失去意义。结论是"技术上可改、实践中不值得改",建议建库时定死并在迁移全程固定。
正确做法是先产出一页"初始化参数清单":页大小、字符集、大小写敏感、兼容模式四项各有明确取值和理由,再执行初始化。失败模式非常典型:为了快速验证按默认参数建库,语法问题修完后才发现字符集不对,只能推倒重建。
第二层:SQL 语法与语义差异——逐类改写
空串与 NULL 的语义差异是迁移后最隐蔽的一类。Oracle 把空串等同于 NULL,达梦对空串与 NULL 的处理与 Oracle 默认行为不一致,两者的比较、NVL/COALESCE、|| 拼接结果都可能和迁移前不同:
-- 迁移前习惯写法,Oracle 中 col = '' 与 col IS NULL 等价
SELECT * FROM t WHERE col = '';
-- 迁移后建议统一改为显式缺失判断,并在入库侧把空串归一为 NULL
SELECT * FROM t WHERE col IS NULL OR col = '';稳妥策略是入库前统一缺失值表示(约定"缺失即 NULL"),查询谓词同时覆盖两种写法做双跑比对,比对 SQL 里专门加一列空值计数。
分页错乱通常有三个来源。一是 ROWNUM 与 ORDER BY 的执行顺序:外层套 ROWNUM、内层排序的经典写法迁移后行为可能变化,必须用子查询先排序再截取,或改用 ROW_NUMBER() 窗口函数。二是分页方言:不同兼容模式下 LIMIT、OFFSET ... FETCH 与 ROWNUM 写法的支持程度不同,要统一成目标库明确支持的写法。三是排序键不唯一:ORDER BY 只按非唯一列排序时,翻页会重复或漏行,无论在哪个数据库都成立,迁移前就该补上唯一排序键。
字符截断与长度语义:Oracle 的 VARCHAR2(n) 有 BYTE/CHAR 两种计量语义,达梦侧长度计量与字符集组合后行为不同,同一列定义在两边的字节上限并不相同。宽字符数据在 GB18030 与 UTF-8 之间迁移时字节数会变,结果就是"应用校验通过、入库报超长"或静默截断。迁移前要按目标字符集重新核算列长度,并在应用侧加长度校验,别依赖数据库报错兜底。
存储过程编译失败集中在几类对象:包头与包体、自定义类型与集合类型、动态 SQL、DBMS_OUTPUT/DBMS_JOB/DBMS_SCHEDULER 等系统包、自治事务与异常处理写法。达梦在兼容模式下支持大部分 PL/SQL,但系统包与个别语法的支持程度有差异,不存在"整体兼容"这回事。正确做法是迁移第一步就做编译验证:把全部存储过程、函数、包、触发器在目标库编译一遍,产出错误清单按错误类型归类修,而不是等联调时逐个撞上。金仓官方的 迁移适配方案同样把兼容性适配单列为迁移阶段工作,思路一致。
大数据基础平台 EasyMR
统一存储、计算与运维,支撑批、流、OLAP 与湖仓一体的国产化大数据底座平台
第三层:驱动与 ORM——最后改应用
数据库层验证通过后,应用侧常见三类问题。其一,JDBC 驱动与连接串未替换或版本不匹配,应用启动即报错,换成目标库随附的 JDBC 驱动并核对连接串参数(字符集、模式、大小写相关参数)。其二,ORM 方言与分页插件未切换:MyBatis 的分页拦截器、Hibernate 的方言类还按 Oracle 生成 SQL,症状是翻页条数不稳定或生成目标库不认的语法,自增主键、序列取值(NEXTVAL 写法)也要逐个核对。其三,函数与格式串差异:TO_DATE/TO_CHAR 的格式模型、日期加减、空值默认值等行为差异会在运行期以"数据不对"的形式暴露,建议在双跑比对阶段把这些函数覆盖到。
国产信创
面向国产化替代与信创改造场景,支撑数据平台平滑迁移、统一纳管、性能保障与安全可控。
双跑比对怎么设计
比对不是"两边跑一下看看",而是把几组固定 SQL 在两边执行后逐项对照:行数一致;每列空值计数一致(专门暴露空串语义差异);数值列合计与校验和一致;字符列按目标字符集做长度分布对比(暴露截断与超长);分页边界样本(首页、尾页、跨页重叠区间)结果一致(暴露分页错乱)。核心查询形态大致如下:
SELECT COUNT(*) AS row_cnt,
SUM(CASE WHEN col IS NULL THEN 1 ELSE 0 END) AS null_cnt,
SUM(CASE WHEN col = '' THEN 1 ELSE 0 END) AS empty_cnt,
SUM(numeric_col) AS sum_val
FROM t;把这几组 SQL 固化成迁移工具链里的检查项,每轮结构或参数调整后自动重跑并留档,作为验收依据。注意空值计数与空串计数要分开统计,二者在两边的差异本身就是迁移风险的直接度量。
建议的迁移顺序
- 先按 Oracle 现状产出初始化参数清单(页大小、字符集、大小写敏感、兼容模式),再初始化实例。
- 用编译验证清单修完全部存储过程与函数对象,产出按错误类型归类的适配记录。
- 双跑比对:行数、空值计数、关键列校验和、分页边界样本(首页、尾页、跨页)。
- 应用层回归:驱动、方言、分页插件、函数行为,重点跑空值与分页相关用例。
- 运行期持续对比一段时间后再下线老库。
面向党政、央国企、金融等关键领域的国产化迁移,通常需要覆盖从国外数据库、大数据平台到国产技术栈的迁移、适配与运行保障,并尽量兼容既有开发与调度习惯、在运行侧统一纳管多种国产引擎和数据库;把上述三层清单沉淀为迁移工具链的一部分,可以显著降低后续批次的重复踩坑成本。
迁移实践记录(参考)里的问题分布也大致符合这个顺序:参数层问题代价最高、语法层问题数量最多、驱动层问题最好修。按层推进,返工最少。


