注册
MySQL - DM SQL兼容性改造
专栏/技术分享/ 文章详情 /

MySQL - DM SQL兼容性改造

,,, 2026/08/21 274 0 0
摘要

MySQL - DM SQL兼容性改造

使用达梦数据迁移工具(DTS)将 MySQL 数据库迁移到达梦(DM)时,总有一些改造是工具无法自动完成的,需要人工处理报错。最容易踩坑的地方是存储过程与函数这类过程化SQL,其次是分区表、系统表查询和数据类型。

本文整理了迁移过程中常见的一批兼容性改造点。如无特殊说明,前者是 MySQL 语法,后者是达梦(DM)语法


一、语法层面的改造

1. 引号与字符串

达梦(Oracle 风格)对引号的规则比 MySQL 严格,迁移时最先要改的就是引号:

  • 函数调用不能加引号:MySQL 中把函数名当字符串写的情况,在达梦中必须去掉引号,否则会被当作普通字符串,而不是调用函数。
-- MySQL 中 'now()' 是字符串,达梦中要调用当前时间函数必须去掉引号 'now()' -- 字符串,不是函数 now() -- 正确调用当前时间函数
  • 双引号的语义不同:MySQL 默认把双引号 "..." 也当作字符串定界符(取决于 sql_mode 是否开启 ANSI_QUOTES);而达梦与 Oracle 一致,双引号只用来引用标识符(表名、列名),字符串一律用单引号。迁移时所有字符串常量都要检查一遍引号类型。
-- MySQL 双引号可以是字符串 SELECT "abc"; -- DM 双引号是标识符,会去查名为 abc 的列,语义完全不同 SELECT "abc";
  • INTERVAL 字面量写法不同:MySQL 中 INTERVAL 1 DAY 的数值不带引号,达梦要求写成字符串形式 INTERVAL '1' DAY
-- MySQL INTERVAL 1 DAY -- DM INTERVAL '1' DAY

2. 字符集

MySQL 在建表、建字段时常需要显式指定 CHARACTER SETCOLLATE;达梦实例默认使用 UTF-8 字符集(初始化实例时配置),一般无需再指定,迁移时删掉这些子句即可。

-- MySQL 需要显式指定字符集 CREATE TABLE t (name VARCHAR(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci); -- DM 默认 UTF-8,直接建表即可 CREATE TABLE t (name VARCHAR(64));

此外,mysql 在建表时要 指定innodb 引擎 ,dm中无,直接删除

3. 变量赋值方式

存储过程中给变量赋值的语法不同:

  • MySQL:SET 变量 = 值,或 SELECT ... INTO
  • 达梦:变量 := 值(与 Oracle PL/SQL 一致),也可以 SELECT ... INTO 变量
-- MySQL SET total = total + 1; -- DM total := total + 1;

4. 变量声明位置

MySQL 的 DECLARE 写在 BEGIN ... END 体内、语句之前;而达梦(Oracle 风格)要求所有变量在独立的 DECLARE 段中集中声明,DECLARE 段位于过程/函数体之前。

-- MySQL:DECLARE 在 BEGIN 体内 BEGIN DECLARE A INT; DECLARE B INT; SET A = 1; END -- DM:DECLARE 段在 BEGIN 之前 DECLARE A INT; B INT; BEGIN A := 1; ... END;

5. 函数/过程的代码块结构(AS / IS)

达梦要求:在函数或过程声明之后、代码块之前,必须有一个 AS(或 IS)关键字,MySQL 没有这个要求。这也是 DTS 迁移后最常见的语法报错之一。

-- DM 正确的函数结构 CREATE OR REPLACE FUNCTION GET_NAME(V1 IN VARCHAR(64)) RETURN VARCHAR(64) AS BEGIN RETURN V1; END;

6. 返回值声明:RETURNS 与 RETURN

MySQL 函数用 RETURNS 类型 声明返回值,达梦用 RETURN 类型

-- MySQL CREATE FUNCTION f1(v INT) RETURNS INT BEGIN RETURN v * 2; END; -- DM CREATE FUNCTION f1(v IN INT) RETURN INT AS BEGIN RETURN v * 2; END;

7. 日期转换函数

MySQL 的 str_to_date() 在达梦中对应 to_date(),两者的格式符体系完全不同,这是最容易忽略的坑:

语义 MySQL 达梦
%Y YYYY
%m MM
%d DD
时分秒 %H:%i:%s HH24:MI:SS
-- MySQL str_to_date('2026-08-17 10:30:00', '%Y-%m-%d %H:%i:%s') -- DM to_date('2026-08-17 10:30:00', 'YYYY-MM-DD HH24:MI:SS')

8. CASE … WHEN 表达式

-- MySQL 简单 CASE CASE V WHEN 1 THEN 'A' WHEN 2 THEN 'B' ELSE 'C' END -- DM 推荐改写为搜索式 CASE CASE WHEN V = 1 THEN 'A' WHEN V = 2 THEN 'B' ELSE 'C' END

9. 函数参数声明顺序

MySQL 把参数模式写在参数名前面IN V1 VARCHAR(64)),达梦(Oracle 风格)把模式写在参数名后面V1 IN VARCHAR(64)):

-- MySQL FUNCTION(IN V1 VARCHAR(64)) -- DM FUNCTION(V1 IN VARCHAR(64))

参数模式有三种:IN(输入)、OUT(输出)、IN OUT(输入输出),迁移时注意全部调整位置。另外 MySQL 可以不写模式(默认 IN),达梦中建议显式写出。

10. 提前退出代码块:LEAVE 与 GOTO

MySQL 可以用 LEAVE 标签 直接跳出指定代码块(类似 break);达梦没有 LEAVE 语法,需要用 GOTO 跳转实现:

-- MySQL mylabel: BEGIN ... LEAVE mylabel; -- 直接跳出 mylabel 块 END mylabel; -- DM BEGIN ... GOTO POINT; -- 跳转到 POINT 标签 ... -- 这段会被跳过 <<POINT>> NULL; -- 标签后必须跟一条可执行语句,否则报错 END;

实用提醒:达梦中 GOTO 的目标标签之后必须紧跟一条可执行语句(通常写 NULL; 占位),否则编译报错。

11. 插入或更新:ON DUPLICATE KEY 与 MERGE

MySQL 的 INSERT ... ON DUPLICATE KEY UPDATE(经典 upsert)在达梦中要改写为 Oracle 风格的 MERGE INTO 语句。改写要点:

  1. USING 子查询提供"待写入的新数据"(可以直接 SELECT ... FROM DUAL,或来自其他表);
  2. ON 指定匹配条件(对应 MySQL 的冲突键);
  3. 匹配上则 UPDATE,匹配不上则 INSERT
-- MySQL INSERT INTO TAB (C1, C2) VALUES (V1, V2) ON DUPLICATE KEY UPDATE C2 = VALUES(C2); -- DM:改写为 MERGE MERGE INTO TAB T USING (SELECT V1 AS C1, V2 AS C2 FROM DUAL) S -- 待写入的新值 ON (T.C1 = S.C1) -- 匹配条件:C1 冲突则更新 WHEN MATCHED THEN UPDATE SET T.C2 = S.C2 WHEN NOT MATCHED THEN INSERT (T.C1, T.C2) VALUES (S.C1, S.C2);

12. 循环结构

WHILE 循环的结束关键字不同:MySQL 用 DO ... END WHILE,达梦用 LOOP ... END LOOP

-- MySQL WHILE i <= 10 DO ... END WHILE; -- DM WHILE i <= 10 LOOP ... END LOOP;

达梦还支持 FOR ... LOOP(类似 Oracle)和 LOOP ... END LOOP(无限循环 + 条件退出),MySQL 没有对应的 FOR 循环语法,迁移时可选择改写。

13. 退出循环:LEAVE / EXIT,ITERATE / CONTINUE

  • MySQL 的 LEAVE 退出循环 → 达梦用 EXIT
  • MySQL 的 ITERATE 跳过本次循环、进入下一次 → 达梦用 CONTINUE
-- MySQL WHILE ... DO IF c > 10 THEN LEAVE; END IF; IF c = 5 THEN ITERATE; END IF; END WHILE; -- DM WHILE ... LOOP IF c > 10 THEN EXIT; END IF; -- 退出循环 IF c = 5 THEN CONTINUE; END IF; -- 跳过本次,进入下一次迭代 END LOOP;

14. 错误码与错误信息

异常处理中取错误码、错误信息的内置标识符不同:

含义 MySQL 达梦
错误码 MYSQL_ERRNO SQLCODE
错误信息 MESSAGE_TEXT SQLERRM
-- DM 在异常块中取出错误信息 EXCEPTION WHEN OTHERS THEN V_ERR_CODE := SQLCODE; -- 错误码 V_ERR_MSG := SQLERRM; -- 错误信息

15. 异常处理机制:HANDLER 与 EXCEPTION

这是两种数据库差异最大的地方:MySQL 使用 DECLARE ... HANDLER 语句声明错误处理程序,达梦则采用接近 Oracle 的 EXCEPTION 异常块机制。

-- MySQL:声明式错误处理 DECLARE EXIT HANDLER FOR 错误名 BEGIN 错误处理代码块 END; -- DM:异常块捕获 DECLARE MY_ERR EXCEPTION; -- 可选:自定义异常名 BEGIN ... EXCEPTION WHEN NO_DATA_FOUND THEN -- 捕获内置"无数据"异常 DONE := 1; WHEN MY_ERR THEN ... -- 处理自定义异常 WHEN OTHERS THEN -- 兜底:其他所有异常 ... END;

达梦内置常用异常:NO_DATA_FOUND(无数据)、TOO_MANY_ROWS(返回多行)、ZERO_DIVIDE(除零)、VALUE_ERROR(值错误)等。

CONTINUE HANDLER 没有直接对应:MySQL 的 DECLARE CONTINUE HANDLER(捕获异常后继续往下执行)在达梦中没有等价语法,因为达梦的 EXCEPTION 块一旦捕获异常,控制流就会跳出当前 BEGIN 块。要模拟"继续执行"的效果,需要把可能出错的语句单独包一层内层代码块,让异常在内层块中被吞掉,外层逻辑继续运行:

-- MySQL DECLARE CONTINUE HANDLER FOR SQLEXCEPTION SET @has_error = 1; ... INSERT INTO LOG_TABLE ...; -- 出错也不中断,继续往下执行 -- DM:用嵌套块模拟 CONTINUE BEGIN -- 外层块 BEGIN -- 内层块:只包住可能出错的语句 INSERT INTO LOG_TABLE ...; EXCEPTION WHEN OTHERS THEN NULL; -- 吞掉异常,不做任何事 END; -- 后续语句照常执行 ... END;

16. 分区表

MySQL 与达梦的分区表在概念和语法上差异都很大,迁移前需要先了解四个核心区别:

  1. 分区:MySQL 的分区由存储引擎(InnoDB)内部管理,表现为逻辑分区;达梦是物理分区,每个分区可以指定存放在不同的表空间,物理隔离更彻底。
  2. 分区键限制:达梦的分区键只支持列,不支持列上的表达式(如 MySQL 的 TO_DAYS(data_date))。如果必须按表达式分区,需要先创建一个计算列(生成列),再以该列作为分区键。
  3. 间隔分区:MySQL 不支持间隔分区,按天/按月分区需要手工预建或用定时任务维护;达梦支持 INTERVAL 间隔分区,新数据到达时自动创建分区,省心很多。
  4. 唯一键/主键与分区键的关系
    • MySQL:表上的每一个唯一键(包括主键)都必须包含全部分区列
    • 达梦:仅要求分区键列包含在主键中(分区键是主键的子集),普通唯一索引不受此限制。

对比示例——按天分区:

-- MySQL:必须手工预建每天的分区,且主键必须包含分区键 CREATE TABLE YOUR_TABLE ( ID INT AUTO_INCREMENT, DATA_DATE DATE NOT NULL, -- 分区键 PRIMARY KEY (ID, DATA_DATE) -- 主键必须包含分区键 ) PARTITION BY RANGE (TO_DAYS(DATA_DATE)) ( -- 用 TO_DAYS 把日期转为天数 PARTITION P20260801 VALUES LESS THAN (TO_DAYS('2026-08-02')), PARTITION P20260802 VALUES LESS THAN (TO_DAYS('2026-08-03')), PARTITION P20260803 VALUES LESS THAN (TO_DAYS('2026-08-04')), ... -- 需要手工列出后续每一天的分区 PARTITION PMAX VALUES LESS THAN MAXVALUE -- 建议保留一个兜底分区 ); -- DM:间隔分区,数据到达自动建分区 CREATE TABLE YOUR_TABLE ( ID INT, DATA_DATE DATE NOT NULL, -- 分区键,必须是日期/时间类型 PRIMARY KEY (ID, DATA_DATE) -- 分区键必须包含在主键内 ) PARTITION BY RANGE (DATA_DATE) -- 直接按日期列分区,无需表达式 INTERVAL (NUMTODSINTERVAL(1, 'DAY')) -- 核心:以 1 天为间隔自动建分区 ( PARTITION P_START VALUES LESS THAN (TO_DATE('2026-08-01', 'YYYY-MM-DD')) -- 必须定义一个起始分区 );

迁移提示:达梦分区键只认列,MySQL 里 PARTITION BY RANGE (TO_DAYS(...)) 这类写法要改成直接按日期列分区 + INTERVAL 间隔;起始分区是必需的,且建议保留 MAXVALUE 兜底分区(间隔分区无需,普通 RANGE 分区需要)。


二、系统表(数据字典)的差异

迁移后,原来基于 MySQL 系统表(INFORMATION_SCHEMA)写的数据字典查询,在达梦中要换成达梦自己的系统视图。常见映射如下(上面的表名对应,下面是列名对应):

-- 查看库中有哪些表 INFORMATION_SCHEMA.TABLES -> DBA_TABLES -- 表所属的库/模式 TABLE_SCHEMA -> OWNER -- 查看约束(含主键、唯一键、外键、检查约束) INFORMATION_SCHEMA.KEY_COLUMN_USAGE -> DBA_CONSTRAINTS -- 约束类型:MySQL 用 CONSTRAINT_NAME 区分,达梦用 CONSTRAINT_TYPE(P=主键、U=唯一、R=外键、C=检查) CONSTRAINT_NAME -> CONSTRAINT_TYPE
-- 查看有哪些列 INFORMATION_SCHEMA.COLUMNS -> DBA_TAB_COLUMNS

实际使用的查询示例:

-- MySQL:查看当前数据库下的所有表(使用 database() 或指定库名) SELECT TABLE_SCHEMA AS OWNER, TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = 'SYSDBA'; -- MySQL:查看 TEST 表的所有列及类型 SELECT COLUMN_NAME, DATA_TYPE, CHARACTER_MAXIMUM_LENGTH AS DATA_LENGTH FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = 'SYSDBA' AND TABLE_NAME = 'TEST'; -- MySQL:查看 TEST 表的约束及类型(主键、唯一、外键、检查) SELECT CONSTRAINT_NAME, CONSTRAINT_TYPE FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE CONSTRAINT_SCHEMA = 'SYSDBA' AND TABLE_NAME = 'TEST'; -- MySQL:查找表中的自增列(EXTRA 包含 'auto_increment') SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = 'SYSDBA' AND TABLE_NAME = 'TEST' AND EXTRA LIKE '%auto_increment%'; -- 达梦:查看 SYSDBA 模式下的所有表 SELECT OWNER, TABLE_NAME FROM DBA_TABLES WHERE OWNER = 'SYSDBA'; -- 达梦:查看 TEST 表的所有列及类型 SELECT COLUMN_NAME, DATA_TYPE, DATA_LENGTH FROM DBA_TAB_COLUMNS WHERE OWNER = 'SYSDBA' AND TABLE_NAME = 'TEST'; -- 达梦:查看 TEST 表的约束及类型 SELECT CONSTRAINT_NAME, CONSTRAINT_TYPE FROM DBA_CONSTRAINTS WHERE OWNER = 'SYSDBA' AND TABLE_NAME = 'TEST'; -- 达梦:查找表中的自增列(SYSCOLUMNS.INFO2 的 bit0 为 1 表示自增) SELECT A.NAME FROM SYSCOLUMNS A, DBA_OBJECTS B WHERE A.ID = B.OBJECT_ID AND B.OBJECT_NAME = 'TEST' AND B.OWNER = 'SYSDBA' AND A.INFO2 = 1;

两个补充说明:

  • 想查"哪一列属于哪个约束"(对应 MySQL 的 KEY_COLUMN_USAGE),达梦要用 DBA_CONS_COLUMNS 视图(DBA_CONSTRAINTS 只保存约束本身的信息);
  • DBA_* 视图要求当前用户有相应权限,权限不足时可换 ALL_*USER_* 系列视图。

三、数据类型差异

MySQL 达梦(DM) 说明
BIGINT UNSIGNED BIGINTDECIMAL 达梦没有无符号类型。数据范围不超过有符号 BIGINT(约 ±9.2×10¹⁸)直接用 BIGINT;超出则用 DECIMAL(DECIMAL(20,0) 之类)
LONGTEXT / MEDIUMTEXT / TINYTEXT CLOB 大文本用 CLOB
LONGBLOB / MEDIUMBLOB / TINYBLOB BLOB 大二进制用 BLOB
BOOLEAN / BOOL BIT 达梦表列不支持布尔类型,用 BIT 存储 0/1(存储过程内部可使用 BOOLEAN)
TEXT CLOB 或大 VARCHAR 达梦单列 VARCHAR 最大 8188 字节,超出建议用 CLOB

补充:达梦的 DECIMAL 与 MySQL 的 DECIMAL 语义一致,但迁移 BIGINT UNSIGNED 时,若原数据实际范围不大,优先用 BIGINT(省空间、性能好),只有确认会超出有符号范围才用 DECIMAL

评论
后发表回复

作者

文章

阅读量

获赞

扫一扫
联系客服