MySQL 迁移达梦全流程实战:DTS 七步图解 + 对象名全大写适配(附踩坑清单)

简介: 本文详解MySQL迁移至达梦(DM8)时“表存在却报不存在”的核心坑:达梦默认大小写敏感,不加引号的`sys_user`被转为`SYS_USER`,而DTS若勾选“保留大小写”则建出`"sys_user"`,导致匹配失败。关键解决方案是迁移前设`CASE_SENSITIVE=Y`、迁移时**取消勾选“保留对象名大小写”**,使对象统一存为大写,应用SQL无需改写即可正常运行。(239字)

前言:迁完之后,表"不存在"了

先说一个几乎每个从 MySQL 迁达梦的人都会踩的坑。

数据迁完,连上库,表也在,但你应用的 SQL 一跑就报:

第1行附近出现错误:
无效的表或视图名[SYS_USER]

你打开 DM 管理工具,表明明在,名字叫 sys_user,小写,一个字母不差。可 SQL 里写 select * from sys_user 就是不认。

原因不在数据,在标识符的大小写规则。

达梦(DM8)对标识符的处理规则是:

写法 DM 的处理结果
select * from sys_user(不加引号) 自动转成 SYS_USER 去匹配
select * from "sys_user"(加双引号) 严格按 sys_user 匹配
建表时 create table sys_user(...) 实际存成 SYS_USER
建表时 create table "sys_user"(...) 实际存成 sys_user

也就是说:DM 实例默认是"大小写敏感"的(CASE_SENSITIVE=Y),不加引号的标识符一律折叠成大写。

而 MySQL 在 Linux 下默认 lower_case_table_names=0,表名原样存成小写。DTS 迁移时如果原样保留,就会建出 "sys_user" 这种带引号的小写对象。此时应用里不带引号的 sys_user 被折叠成 SYS_USER,和 "sys_user" 匹配不上 —— 于是"表不存在"。

结论只有一句话:要让应用 SQL 不加引号也能跑,迁移时就必须让对象名变成"不带引号的大写"。

下面这套流程,就是把这件事一次做对。


一、环境与迁移前准备

1.1 环境信息

项目 版本
源库 MySQL 8.0.26(驱动 mysql-connector-java 8.0.x)
目标库 达梦 DM8(JDBC 驱动 DmJdbcDriver18.jar)
迁移工具 达梦数据迁移工具 DTS(DM 安装自带,Windows 版 DM数据迁移工具.exe)
迁移规模 1 个库 → 1 个模式,41 张表

1.2 达梦侧:实例参数必须在建库前想清楚

这一步最容易被跳过,但它是后面所有麻烦的根源。DM 有两个参数建库后无法修改,只能重建实例:

参数 建议值 说明
CASE_SENSITIVE Y(默认,敏感) 保持敏感 + 迁移时转大写,是与 MySQL 习惯最贴合的方案;设为 N 则完全不区分大小写,SQL 怎么写都能查到,但会失去精确控制
LENGTH_IN_CHAR 1 强烈建议设为 1。设为 0(默认)时 VARCHAR(n) 的 n 是字节数;UTF-8 下一个汉字占 3 字节,VARCHAR(10) 只能存 3 个汉字,中文数据会被截断或报错
CHARSET 1(UTF-8) 对齐 MySQL 的 utf8mb4
PAGE_SIZE 16 或 32 按数据量选,一般 16 够用

如果你用的是图形化 dbca.sh / DM 数据库配置助手,这两个选项在"初始化参数"页里就是勾选框,一定要在建实例那一步勾上。

这一条和第 1.1 节的"字符长度映射 1.0"是配套的:如果实例已经按字节算了,迁移时就得把映射系数调大(见 3.3)。

1.3 达梦侧:创建用户与模式

DM 中"用户"和"模式(Schema)"一一同名,一个用户默认拥有一个同名模式。建议先手工建好目标用户,不要让 DTS 自动建,避免权限和命名失控:

-- 用 SYSDBA 登录执行
CREATE USER "AV_CAPTURE_ADMIN" IDENTIFIED BY "Dm@12345678";
GRANT DBA TO "AV_CAPTURE_ADMIN";

命名建议:模式名尽量用下划线(AV_CAPTURE_ADMIN),不要用中划线(AV-CAPTURE-ADMIN)。因为含 - 的标识符在 SQL 里必须加双引号才能用,否则被当成减号,select * from AV-CAPTURE-ADMIN.sys_user 直接语法错误。这会给你后面的所有运维操作埋雷。本文示例沿用了图中的中划线写法,实际项目建议改掉。

1.4 MySQL 侧:迁移前的 4 项体检

别急着点迁移,先用这几条 SQL 把源库摸一遍。

① 找出 DM 不支持或不兼容的类型

SELECT TABLE_NAME, COLUMN_NAME, COLUMN_TYPE, DATA_TYPE, IS_NULLABLE, COLUMN_DEFAULT, EXTRA
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = 'av_capture_admin'
  AND DATA_TYPE IN ('enum','set','json','tinyint','mediumint','unsigned bigint','year')
ORDER BY TABLE_NAME, ORDINAL_POSITION;

重点关注 enum / set / json / unsigned —— 这几类 DM 没有等价类型,DTS 的默认映射往往会降级成 VARCHAR 或丢掉取值范围约束,需要人工确认。

② 扫一遍零日期(经典杀手)

MySQL 允许 0000-00-00 00:00:00,DM 直接拒绝写入。迁之前必须清掉:

-- 先用这个查出哪些表哪些字段有零日期
SELECT 'UPDATE `' || TABLE_NAME || '` SET `' || COLUMN_NAME || '` = NULL WHERE `' || COLUMN_NAME || '` = ''0000-00-00 00:00:00'';' AS fix_sql
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = 'av_capture_admin'
  AND DATA_TYPE IN ('datetime','timestamp')
  AND IS_NULLABLE = 'YES';

③ 确认字符集统一

SELECT TABLE_NAME, TABLE_COLLATION
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = 'av_capture_admin' AND TABLE_COLLATION NOT LIKE 'utf8mb4%';

④ 记录基线数量,迁移后要核对

SELECT TABLE_NAME, TABLE_ROWS
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = 'av_capture_admin'
ORDER BY TABLE_NAME;

TABLE_ROWS 是估算值,仅用于粗筛;精确值请用 count(*) 逐表统计,或迁移后按主键做 max(id) 比对。


二、DTS 迁移七步图解

打开 DM数据迁移工具,左上角新建迁移工程,选择 MySQL → DM,然后按下面的顺序走。

第 1 步:配置源库(MySQL)连接

在这里插入图片描述

填写主机名、端口 3306、用户名、口令、数据库名即可。重点看下面那个自定义 URL,它决定了类型识别的准确度:

jdbc:mysql://192.168.32.10:3306/av_capture_admin?tinyInt1isBit=false&transformedBitIsBoolean=false&useSSL=false

三个参数的作用,以及建议补上的四个:

参数 作用
tinyInt1isBit=false 让 tinyint(1) 不要被当成 Boolean 读出来。MySQL 里 tinyint(1) 常被用作状态位,如果映射成布尔,迁到 DM 会变成 BIT,应用里 status = 1 直接报错
transformedBitIsBoolean=false 同理,禁止把 BIT 转成 Boolean
useSSL=false 内网迁移关掉 SSL,避免握手失败
allowPublicKeyRetrieval=true MySQL 8 建议加,否则 caching_sha2_password 认证会报 Public Key Retrieval is not allowed
serverTimezone=Asia/Shanghai 建议加,避免 datetime 迁移后整体偏移 8 小时
characterEncoding=utf8 建议加,防中文乱码
zeroDateTimeBehavior=convertToNull 兜底:万一还有零日期,自动转 NULL 而不是中断迁移

如果你不确定驱动版本,点「指定驱动」手工指定本机的 mysql-connector-java-8.0.xx.jar。图中当前驱动版本为 26.10(即 8.0.26),与源库一致,最好保持匹配。

第 2 步:配置目的库(DM)连接

在这里插入图片描述

字段 填写
主机名 localhost(或 DM 服务器 IP)
端口 5236(DM 默认端口)
验证方式 达梦服务器验证
用户名 SYSDBA(先用高权限账号,避免建表建约束时权限不足)
自定义 URL jdbc:dm://localhost:5236

同样点「指定驱动」确认指向 DM 安装目录下的 DmJdbcDriver18.jar(一般在 DM8/drivers/jdbc/)。

第 3 步:迁移选项 —— 全文最关键的一个勾

在这里插入图片描述

「获取迁移对象方式」选 选择迁移对象。

然后是「迁移策略」,这里有三个选项,请照着设:

选项 设置 原因
出错后继续执行 ✅ 勾选 41 张表批量迁,个别表失败不至于中断全部;失败项会在日志里单独列出,事后补迁
保留对象名大小写 ❌ 不勾(默认) 这就是"转大写"的开关。不勾 → DTS 把源端小写对象名统一转成大写、并去掉引号,落库为 SYS_USER 这种形式;勾上 → 原样保留小写并加引号,落库为 "sys_user",应用 SQL 全废
使用默认数据类型映射关系 ✅ 勾选 走 DM 内置映射表;如有个别类型要改,点「查看默认类型映射关系」后单独调整

再往下两个数值:

  • 字符长度映射:图中是 1.0。这个系数的含义是"目标字符串长度 = 源长度 × 系数"。
    • 若 DM 实例 LENGTH_IN_CHAR=1(按字符计数)→ 保持 1.0 即可;
    • 若 DM 实例是默认的按字节计数,而你的业务有中文 → 请改成 3.0,否则 varchar(50) 只能装 16 个汉字,迁完就是一堆"字符串截断"。
  • 迁移任务最大并发数:2。表多、机器好的话可以调到 4~8,能明显加速;但并发过高时约束依赖(外键)容易报错,建议先小后大。

第 4 步:指定源模式与目的模式

在这里插入图片描述

左边勾选要迁移的源模式(图中 av-capture-admin,该实例下有 12 个库,只勾这一个),右边会列出对应的目的模式。

两个注意点:

  1. 目的模式会跟着"转大写"一起变形。因为没勾"保留对象名大小写",目的模式名也会被写成 AV-CAPTURE-ADMIN(大写)。所以 1.3 里用 CREATE USER "AV_CAPTURE_ADMIN" 建出来的用户,模式名对不上,DTS 会自动再建一个模式,导致你手工建的用不上。要么目的模式名字手工改成你建好的那个,要么干脆让 DTS 建、后面再授权。
  2. 模式名带中划线的话,所有 SQL 都得加双引号,参考 1.3 的建议尽早改掉。

第 5 步:指定迁移对象

在这里插入图片描述

切到「所有对象类型」,逐个勾选表、视图、序列等。图中源模式有 41 张表,可以点「全选」。

在这里插入图片描述

勾完确认右下角计数是 41。建议第一轮先只勾 2~3 张结构简单、有代表性的表试迁,跑通了再全量 —— 全量失败后清理现场(删模式重建)比重迁一次还费时间。

第 6 步:转换对象(设置数据转换规则)

在这里插入图片描述

这一步决定"迁过去长什么样"。选中任意一张表点「转换」,弹出的对话框里逐项配置:

左侧分组

分组 建议 说明
表定义 ✅ 勾选 展开后含:包含主键、包含约束、保留自增列属性、包含列注释
└ 如果对象已存在 先删除表 重迁时最省心,避免"表已存在"批量失败
└ 级联删除(CASCADE) ✅ 勾选 有外键依赖时,删除顺序不会被卡住
数据 ✅ 勾选 迁移表数据
└ 如果数据已存在 先删除 防止重迁造成数据重复
└ 保留主键约束 ⚠️ 谨慎 数据量大时取消勾选,先迁数据再统一建主键/索引,速度能快几倍。但要注意自增列冲突,见第五节
视图 ✅ 勾选 DM 支持视图迁移,但 MySQL 特有函数(IFNULL、DATE_FORMAT)需迁移后改写
索引 ✅ 勾选 含唯一索引、普通索引、前缀索引
约束 ✅ 勾选 外键、唯一约束、CHECK
触发器 / 存储过程 视情况 MySQL 触发器语法与 DM 差异较大,多数场景建议不迁,迁移后按 DM 语法重写
使用 IDENTITY 自增列 ✅ 勾选 把 AUTO_INCREMENT 转成 DM 的 IDENTITY(1,1),必勾
ON UPDATE 触发器 ❌ 不勾 MySQL 的 ON UPDATE CURRENT_TIMESTAMP 在 DM 里没有原生对应,勾了会额外生成触发器,容易和业务触发器打架。改为应用层赋值或用 DEFAULT + 触发器手工实现

右侧「数据」分组的读写参数(每次读取行数 2048、每次插入行数 2048、合并行数 3)一般保持默认。数据量大、网络好的话,可以开启「快速加载」(多实例/批量),提速明显。

第 7 步:审阅 —— 在这里验证大写是否生效

在这里插入图片描述

这一步是全文的"验收点",务必逐行看对象列表。

图中可以看到,任务摘要已经如实反映:

出错后继续执行:  是
迁移任务最大并发数:2
保留对象名大小写:  否          ← 关键
字符长度映射:     1.0
使用默认数据类型映射:是

而下面的对象映射列表,正是我们想要的结果:

av-capture-admin.gen_table          --> "AV-CAPTURE-ADMIN"."GEN_TABLE"
av-capture-admin.gen_table_column   --> "AV-CAPTURE-ADMIN"."GEN_TABLE_COLUMN"
av-capture-admin.sys_config         --> "AV-CAPTURE-ADMIN"."SYS_CONFIG"
av-capture-admin.sys_dict_data      --> "AV-CAPTURE-ADMIN"."SYS_DICT_DATA"
...

判断标准:箭头右侧是不是全大写。只要是大写,应用里不加引号的 select * from sys_config 就能正确命中;如果右侧是 "gen_table" 这种小写带引号的,说明第 3 步的勾没取消,回去重来。

确认无误 → 点「完成」开始执行 → 看日志。中途失败的表会被记下来但不中断,跑完后按提示补迁即可。


三、迁移后立刻做的 6 项验证

别急着改代码,先把库验一遍。

① 对象名是不是真的大写

-- 期望:全部为大写
SELECT OWNER, TABLE_NAME FROM DBA_TABLES
WHERE OWNER = 'AV-CAPTURE-ADMIN'
ORDER BY TABLE_NAME;

-- 如果有小写的,会在这条里出现
SELECT TABLE_NAME FROM DBA_TABLES
WHERE OWNER = 'AV-CAPTURE-ADMIN' AND TABLE_NAME <> UPPER(TABLE_NAME);

② 对象数量对齐

SELECT COUNT(*) FROM DBA_TABLES  WHERE OWNER = 'AV-CAPTURE-ADMIN';
SELECT COUNT(*) FROM DBA_VIEWS   WHERE OWNER = 'AV-CAPTURE-ADMIN';
SELECT COUNT(*) FROM DBA_INDEXES WHERE OWNER = 'AV-CAPTURE-ADMIN';

③ 行数逐表比对(用主键列比 max(id) 最快,比 count(*) 省时间)

-- 在 DM 侧执行,和 MySQL 侧 max(id) 对照
SELECT 'SELECT ''' || TABLE_NAME || ''' T, COUNT(*) C FROM "' || OWNER || '"."' || TABLE_NAME || '" UNION ALL'
FROM DBA_TABLES WHERE OWNER = 'AV-CAPTURE-ADMIN' AND TABLE_NAME = 'SYS_USER';

④ 中文有没有被截断

-- 建库时 LENGTH_IN_CHAR=0 的,这里会暴露问题
SELECT TABLE_NAME, COLUMN_NAME, DATA_LENGTH, CHAR_LENGTH, CHAR_USED
FROM DBA_TAB_COLUMNS
WHERE OWNER = 'AV-CAPTURE-ADMIN' AND CHAR_USED = 'B';   -- B=字节, C=字符

CHAR_USED = 'B' 表示按字节计长。如果业务有中文且已被截断,只能重建实例(LENGTH_IN_CHAR=1)后重迁 —— 这也是为什么 1.2 强调"建库前想清楚"。

⑤ 自增列是否可用

INSERT INTO "AV-CAPTURE-ADMIN"."SYS_CONFIG"(CONFIG_NAME) VALUES('__test__');
SELECT MAX(CONFIG_ID) FROM "AV-CAPTURE-ADMIN"."SYS_CONFIG";
-- 确认新插入的 ID 大于迁移数据的最大 ID,否则删掉测试数据并重置 IDENTITY 种子

⑥ 注释在不在

SELECT TABLE_NAME, COMMENTS FROM DBA_TAB_COMMENTS WHERE OWNER = 'AV-CAPTURE-ADMIN' AND ROWNUM <= 10;
SELECT TABLE_NAME, COLUMN_NAME, COMMENTS FROM DBA_COL_COMMENTS WHERE OWNER = 'AV-CAPTURE-ADMIN' AND ROWNUM <= 20;

四、如果已经迁错了:把对象名批量转大写

假设当初勾了"保留对象名大小写",现在库里全是 "gen_table" 这种小写对象,两条补救路线:

方案 A:重建实例,用不敏感模式(最彻底)

如果还在测试阶段,最快的是删掉模式重来,或者重装实例时把 CASE_SENSITIVE 设为 N。大小写不敏感后,sys_user / SYS_USER / Sys_User 都能命中同一个对象,MySQL 应用几乎零改动。

代价是:失去了对同名不同大小写对象的区分能力(实际项目基本不需要),且实例必须重建。

方案 B:用 DM 动态 SQL 批量重命名(不重建实例)

思路:从数据字典里把"名字不是大写"的对象捞出来,生成 ALTER ... RENAME 脚本,人工 review 后执行。顺序很关键:先表 → 再列 → 再索引 → 最后序列,否则会因依赖关系报错。

第一步:生成重命名表的语句

SELECT 'ALTER TABLE "' || OWNER || '"."' || TABLE_NAME
       || '" RENAME TO "' || UPPER(TABLE_NAME) || '";' AS DDL
FROM DBA_TABLES
WHERE OWNER = 'AV-CAPTURE-ADMIN'
  AND TABLE_NAME <> UPPER(TABLE_NAME);

第二步:生成重命名列的语句

SELECT 'ALTER TABLE "' || OWNER || '"."' || TABLE_NAME
       || '" RENAME COLUMN "' || COLUMN_NAME || '" TO "' || UPPER(COLUMN_NAME) || '";' AS DDL
FROM DBA_TAB_COLUMNS
WHERE OWNER = 'AV-CAPTURE-ADMIN'
  AND COLUMN_NAME <> UPPER(COLUMN_NAME)
ORDER BY TABLE_NAME, COLUMN_ID;

第三步:生成重命名索引的语句

SELECT 'ALTER INDEX "' || OWNER || '"."' || INDEX_NAME
       || '" RENAME TO "' || UPPER(INDEX_NAME) || '";' AS DDL
FROM DBA_INDEXES
WHERE OWNER = 'AV-CAPTURE-ADMIN'
  AND INDEX_NAME <> UPPER(INDEX_NAME);

第四步:生成重命名序列的语句(用了 IDENTITY 或其他序列的话)

SELECT 'ALTER SEQUENCE "' || SEQUENCE_OWNER || '"."' || SEQUENCE_NAME
       || '" RENAME TO "' || UPPER(SEQUENCE_NAME) || '";' AS DDL
FROM DBA_SEQUENCES
WHERE SEQUENCE_OWNER = 'AV-CAPTURE-ADMIN'
  AND SEQUENCE_NAME <> UPPER(SEQUENCE_NAME);

把四段输出复制到一个 .sql 文件里,先备份库(dexp 逻辑导出整个模式),再按 表→列→索引→序列 的顺序执行。

提示:如果你的 DM 版本报 RENAME COLUMN 语法不支持,改用 ALTER TABLE "S"."T" ALTER COLUMN "a" RENAME TO "A";,或者直接在 DM 管理工具里右键对象重命名(适合少量表)。约束名无法直接重命名,一般也不影响业务,忽略即可。

方案 C:应用层加双引号(不推荐)

理论上可以在所有 SQL 里把 sys_user 改成 "sys_user"。实际上这是灾难:MyBatis 的 @Select、JPA 的自动建表、分页插件、动态表名全都要改,改完还绑死了"必须小写"这个前提。只在极少数无法重迁的应急场景考虑。


五、应用侧适配清单(12 类高频差异)

大写的坑填平了,还有一批语法和类型差异在等着。按出现频率排序:

1. 驱动与连接串

# 旧(MySQL)
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/av_capture_admin?useSSL=false&serverTimezone=Asia/Shanghai
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

# 新(DM8)
spring.datasource.url=jdbc:dm://127.0.0.1:5236?schema=AV_CAPTURE_ADMIN
spring.datasource.driver-class-name=dm.jdbc.driver.DmDriver

依赖换成:

<dependency>
    <groupId>com.dameng</groupId>
    <artifactId>DmJdbcDriver18</artifactId>
    <version>8.1.3.140</version>
</dependency>

schema= 参数建议显式指定,避免因用户默认模式不一致导致的"表找不到"。注意这里填的是大小写敏感环境下真实的模式名,和你迁移后的模式保持一致。

2. 反引号 ` 必须处理

MySQL 用反引号包标识符,DM 用双引号(或不要引号)。若代码里有:

select `name`, `status` from `sys_user`

DM 里会报语法错误(把 ` 当非法字符)。批量替换成无引号形式(前提是对象名已大写且不是关键字),或统一换成双引号。

3. 关键字冲突

MySQL 里能直接当列名的,DM 里可能是保留字。常见踩雷列名:

TYPE  LEVEL  SIZE  COMMENT  VALUE  STATUS  ORDER  GROUP  USER  PASSWORD  PATH  ROLE  KEY

处理方式二选一:加双引号(代码全部要改),或迁移时把列名改成 USER_TYPE、ORDER_NO 之类(推荐,一次性解决)。

4. 函数映射

MySQL 达梦
IFNULL(a,b) NVL(a,b)(DM 也兼容 IFNULL)
NOW() / SYSDATE() SYSDATE / NOW()
DATE_FORMAT(d,'%Y-%m-%d') TO_CHAR(d,'YYYY-MM-DD')
STR_TO_DATE(s,'%Y-%m-%d') TO_DATE(s,'YYYY-MM-DD')
GROUP_CONCAT(x) WM_CONCAT(x) / LISTAGG(x,',')
SUBSTRING_INDEX SUBSTR + INSTR 组合
LIMIT 10 OFFSET 20 LIMIT 10 OFFSET 20(DM 兼容模式支持)或 ROWNUM / OFFSET ... FETCH
ON DUPLICATE KEY UPDATE MERGE INTO
INSERT IGNORE 先查后插,或 MERGE INTO

5. 自增列

AUTO_INCREMENT → IDENTITY(1,1)。取刚插入的自增 ID:

-- MySQL
SELECT LAST_INSERT_ID();
-- DM
SELECT IDENT_CURRENT('SYS_USER');   -- 或使用 SCOPE_IDENTITY() / JDBC getGeneratedKeys

MyBatis 的 useGeneratedKeys="true" keyProperty="id" 在 DM 下通常可直接用,但建议实测;不行就显式 SELECT IDENT_CURRENT(...)。

6. 类型映射速查

MySQL 达梦 注意
tinyint(1) TINYINT 一定要配 tinyInt1isBit=false,否则变成 BIT
int unsigned INT / BIGINT DM 无 unsigned,要评估上限,必要时升 BIGINT
bigint unsigned BIGINT / DECIMAL(20) 同上
varchar(n) VARCHAR(n) 见 LENGTH_IN_CHAR 说明
text TEXT / CLOB 大文本建议 CLOB
blob / longblob BLOB / IMAGE 图片流注意驱动
datetime TIMESTAMP 检查时区,见第 1 步 serverTimezone
decimal(m,n) DECIMAL(m,n) 一致
enum('a','b') VARCHAR(n) + CHECK 需手工转换,DTS 一般降级为 VARCHAR
set('a','b') VARCHAR(n) 同上
json TEXT / CLOB DM8 对 JSON 支持有限,应用侧做序列化
year SMALLINT 少见

7. ON UPDATE CURRENT_TIMESTAMP

DM 没有这个语法。三个替代方案:

  • 应用层统一在 update 时赋值(最推荐,逻辑透明);
  • 建 BEFORE UPDATE 触发器;
  • 表中保留字段,由 MyBatis 拦截器填充。

8. 分页

LIMIT ?,? 在 DM 的 MySQL 兼容模式下可用。如果报错,改写成:

SELECT * FROM (
    SELECT t.*, ROWNUM rn FROM (原始SQL) t WHERE ROWNUM <= 20
) WHERE rn > 10;

或者用 PageHelper 的 helperDialect=dm。

9. 事务与隔离级别

DM 默认隔离级别是读提交(READ COMMITTED),与 MySQL InnoDB 默认的可重复读(REPEATABLE READ)不同。如果业务依赖"同一事务内多次读结果一致",需要显式设置:

SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;

10. 视图与存储过程

MySQL 视图里的 IFNULL、DATE_FORMAT、LIMIT,以及存储过程的 DECLARE ... CURSOR、DELIMITER 语法,都无法直接迁。建议视图手工重写(数量通常不多),存储过程按 DM 的 PL/SQL 语法重构。

11. GROUP BY 严格模式

MySQL 8 默认开启 ONLY_FULL_GROUP_BY,DM 行为类似但报错信息不同。若原来靠"宽松模式"跑的非标准 GROUP BY,迁过去会直接失败 —— 这其实是好事,顺手把 SQL 写规范。

12. 大小写混用(再次提醒)

即使对象名已大写,如果代码里有用双引号包小写标识符的地方,同样会失效。全局搜一遍代码里的 " 包裹的 SQL 标识符,统一清理。


六、报错速查表

报错信息 原因 解决
无效的表或视图名[XXX] 对象名大小写不匹配 检查第 3 步是否取消了"保留对象名大小写";或按第四节补救
无效的列名[XXX] 列名未转大写 同上,用 DBA_TAB_COLUMNS 核对
字符串截断 VARCHAR 按字节计长,中文超长 实例 LENGTH_IN_CHAR=1,或迁移时字符长度映射调 3.0
Public Key Retrieval is not allowed MySQL 8 认证插件 URL 加 allowPublicKeyRetrieval=true
无法解析的日期/时间 零日期 0000-00-00 迁前 UPDATE 成 NULL,或加 zeroDateTimeBehavior=convertToNull
语法分析出错 在反引号位置 ` 不支持 反引号换双引号或去掉
第N行附近出现错误: 无效的列名[type] 关键字冲突 列名加双引号或改名
违反唯一性约束 重迁时数据重复 转换对象里勾"数据存在时先删除"
表已存在 重迁未清理 勾"如果对象已存在→先删除表"+"级联删除"
中文乱码 字符集不一致 实例 CHARSET=1,URL 加 characterEncoding=utf8
自增 ID 冲突 IDENTITY 种子未对齐 迁完数据后 SET IDENTITY_INSERT 或重建 IDENTITY 起点
时间差 8 小时 时区未指定 URL 加 serverTimezone=Asia/Shanghai,或迁后统一校正

七、一张图看懂决策路径

迁移前
  │
  ├─ DM 实例已建? ──否──> 建实例时设 CASE_SENSITIVE / LENGTH_IN_CHAR / CHARSET
  │                         (建后不可改,务必想清楚)
  └─ 是
        │
        ├─ 目标:MySQL 应用尽量不改?
        │     ├─ 是 → 迁移时【不勾】"保留对象名大小写" → 对象全大写
        │     │       (推荐,与默认敏感实例匹配)
        │     └─ 否 → 实例 CASE_SENSITIVE=N,怎么写都能查到
        │
        ├─ 是否需要重迁?
        │     ├─ 是 → 第四节方案 B,批量 RENAME(先备份)
        │     └─ 否 → 进入验证
        │
        └─ 验证六项 → 应用改造(驱动/反引号/函数/自增/事务)

结语

回看这次迁移,真正花时间的不是 DTS 那七步点击 —— 工具本身足够成熟,图形化流程也很顺。真正消耗时间的是两类事:

第一类是"不可逆的初始化决策"。 CASE_SENSITIVE、LENGTH_IN_CHAR、CHARSET 这三个参数在实例建好那一刻就定型了。LENGTH_IN_CHAR 判断错的代价是中文数据批量截断,只能推倒重来。

第二类是"沉默的语义漂移"。 迁移工具按部就班地跑完,日志一路绿色,但 tinyint(1) 悄悄变成了 BIT,ON UPDATE CURRENT_TIMESTAMP 悄悄消失了,自增种子悄悄没对齐 —— 这些都不会在迁移时报错,只会在上线第三天以"数据莫名其妙不对"的形式暴露出来。

所以那份"迁移前体检 + 迁移后验证"的清单,比任何一个操作步骤都值钱。工具能帮你搬数据,搬不了语义。

最后留一句话给正在准备迁移的同学:先在测试环境把 41 张表完整跑两遍,第二遍故意勾错"保留对象名大小写"看它怎么坏 —— 见过它怎么坏,你才知道该怎么防。

相关文章
|
14天前
|
Linux 开发工具 iOS开发
Codex保姆级教程(下载+安装+登录+使用,含所有版本,2026最新)
Codex是OpenAI推出的开源AI编程智能体,本地运行,可理解整个代码库,支持自然语言指令改代码、执行命令、跑测试、修Bug,兼容Win/macOS/Linux。(239字)
6210 45
|
2天前
|
云安全 人工智能 安全
|
15天前
|
JavaScript API iOS开发
DeepSeek Harness下载、安装、配置一篇搞定(2026整理,送常用插件)
DeepSeek Harness(DSH)是DeepSeek于2026年开源的MIT协议AI Agent框架,支持插件化组装模型、工具、沙箱与UI,兼容主流大模型。本文提供免Node.js的桌面端安装包(Win/macOS),含详细图文教程及热门插件推荐。
5134 60
|
9天前
|
Linux 开发工具 iOS开发
2026最新codex下载安装保姆级使用教程(下载+安装+登录+使用,含所有历史版本)
本文详解OpenAI Codex国内快速上手指南:提供Win/macOS/Linux全平台免安装包、npm/Homebrew等多方式安装、账号/API Key双登录、简体中文切换,覆盖下载、安装、汉化、使用及高频避坑(如微软商店失败、npm超时等),10分钟即可跑通。
2487 6
|
2天前
|
JSON 前端开发 API
阿里云最新Qwen4-Max大模型介绍:模型定位、特征、能力、适用场景解析
2026 年 11 月 11 日,阿里发布第四代旗舰模型Qwen4-Max预览版,首发千问 AI 平台 Token Plan,并同步接入千问办公、Qoder 等入口。该版本聚焦知识理解、世界感知、编程与 Agentic 四项升级,主打复杂编程、专业办公、前端设计与专业软件操作。预览期至 11 月 25 日 24:00,到期后模型标识下线;限时 2 折、新用户再享 5 折,最低 1 折可体验。
|
7天前
|
人工智能 安全 API
DeepSeek Harness下载、安装、配置一篇搞定(2026整理,送常用插件)
DeepSeek Harness(dsh)是DeepSeek AI于2026年开源的轻量级Agent运行框架,基于Cordis内核,践行“一切皆插件”理念。支持Web/桌面双端,开箱即用;内置多模型接入、丰富插件生态(超3.5万),适配编程、办公、创作等场景。(239字)
1565 1
|
2天前
|
人工智能 前端开发 API
阿里云Qwen4-Max 大模型详解:定位、特征、能力、适用场景与相对Qwen3-Max差异解析
2026年11月11日,阿里发布第四代旗舰模型Qwen4-Max预览版,首发千问AI平台Token Plan并接入千问办公、Qoder等入口。该版本聚焦知识理解、世界感知、编程与Agentic四项升级,主打复杂编程、专业办公、前端设计与专业软件操作等真实任务。预览期为11月11日10:00至11月25日24:00,仅支持Token Plan调用,到期后模型标识下线;限时2折、新用户再享5折,最低1折可体验。

热门文章

最新文章