使用达梦数据迁移工具(DTS)将 MySQL 数据库迁移到达梦(DM)时,总有一些改造是工具无法自动完成的,需要人工处理报错。最容易踩坑的地方是存储过程与函数这类过程化SQL,其次是分区表、系统表查询和数据类型。
本文整理了迁移过程中常见的一批兼容性改造点。如无特殊说明,前者是 MySQL 语法,后者是达梦(DM)语法。
达梦(Oracle 风格)对引号的规则比 MySQL 严格,迁移时最先要改的就是引号:
-- MySQL 中 'now()' 是字符串,达梦中要调用当前时间函数必须去掉引号
'now()' -- 字符串,不是函数
now() -- 正确调用当前时间函数
"..." 也当作字符串定界符(取决于 sql_mode 是否开启 ANSI_QUOTES);而达梦与 Oracle 一致,双引号只用来引用标识符(表名、列名),字符串一律用单引号。迁移时所有字符串常量都要检查一遍引号类型。-- MySQL 双引号可以是字符串
SELECT "abc";
-- DM 双引号是标识符,会去查名为 abc 的列,语义完全不同
SELECT "abc";
INTERVAL 1 DAY 的数值不带引号,达梦要求写成字符串形式 INTERVAL '1' DAY。-- MySQL
INTERVAL 1 DAY
-- DM
INTERVAL '1' DAY
MySQL 在建表、建字段时常需要显式指定 CHARACTER SET、COLLATE;达梦实例默认使用 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中无,直接删除
存储过程中给变量赋值的语法不同:
SET 变量 = 值,或 SELECT ... INTO。变量 := 值(与 Oracle PL/SQL 一致),也可以 SELECT ... INTO 变量。-- MySQL
SET total = total + 1;
-- DM
total := total + 1;
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;
达梦要求:在函数或过程声明之后、代码块之前,必须有一个 AS(或 IS)关键字,MySQL 没有这个要求。这也是 DTS 迁移后最常见的语法报错之一。
-- DM 正确的函数结构
CREATE OR REPLACE FUNCTION GET_NAME(V1 IN VARCHAR(64))
RETURN VARCHAR(64)
AS
BEGIN
RETURN V1;
END;
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;
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')
-- 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
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),达梦中建议显式写出。
MySQL 可以用 LEAVE 标签 直接跳出指定代码块(类似 break);达梦没有 LEAVE 语法,需要用 GOTO 跳转实现:
-- MySQL
mylabel: BEGIN
...
LEAVE mylabel; -- 直接跳出 mylabel 块
END mylabel;
-- DM
BEGIN
...
GOTO POINT; -- 跳转到 POINT 标签
... -- 这段会被跳过
<<POINT>>
NULL; -- 标签后必须跟一条可执行语句,否则报错
END;
实用提醒:达梦中
GOTO的目标标签之后必须紧跟一条可执行语句(通常写NULL;占位),否则编译报错。
MySQL 的 INSERT ... ON DUPLICATE KEY UPDATE(经典 upsert)在达梦中要改写为 Oracle 风格的 MERGE INTO 语句。改写要点:
USING 子查询提供"待写入的新数据"(可以直接 SELECT ... FROM DUAL,或来自其他表);ON 指定匹配条件(对应 MySQL 的冲突键);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);
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 循环语法,迁移时可选择改写。
LEAVE 退出循环 → 达梦用 EXIT;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;
异常处理中取错误码、错误信息的内置标识符不同:
| 含义 | MySQL | 达梦 |
|---|---|---|
| 错误码 | MYSQL_ERRNO |
SQLCODE |
| 错误信息 | MESSAGE_TEXT |
SQLERRM |
-- DM 在异常块中取出错误信息
EXCEPTION
WHEN OTHERS THEN
V_ERR_CODE := SQLCODE; -- 错误码
V_ERR_MSG := SQLERRM; -- 错误信息
这是两种数据库差异最大的地方: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;
MySQL 与达梦的分区表在概念和语法上差异都很大,迁移前需要先了解四个核心区别:
TO_DAYS(data_date))。如果必须按表达式分区,需要先创建一个计算列(生成列),再以该列作为分区键。INTERVAL 间隔分区,新数据到达时自动创建分区,省心很多。对比示例——按天分区:
-- 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 |
BIGINT 或 DECIMAL |
达梦没有无符号类型。数据范围不超过有符号 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。
文章
阅读量
获赞
