从MySQL数据库迁移到达梦DM8数据库踩过的坑

发布时间 2023-06-14 14:48:06作者: MrSuperChaos

写在前面

之前的项目涉及到了旧项目数据库从MySQL迁移至达梦数据库。

达梦实际上和MySQL相比,更类似于Oracle数据,从Oracle迁移过去则问题相对少一些。

DM8官方文档中是支持开启MySQL兼容模式的:

在配置文件dm.ini中找到COMPATIBLE_MODE参数修改为4

达梦官方的文档也在更新各种迁移中可能遇到的问题,笔者写下此篇文章是,该文档比当初笔者做项目迁移时丰富了很多,基本可以解决大多数问题。

https://eco.dameng.com/document/dm/zh-cn/faq/faq-mysql-dm8-migrate.html

group by

MySQL在未开启严格模式下,允许group by模式下可以允许查询中group by 子句中不包含所有查询列,而达梦数据库默认是不可以的,如果之前的SQL语句写的不是很规范的话,这里会带来很多修改工作量。

达梦数据库在开启MySQL兼容模式后可以达到同样效果

关于大小写敏感与否的坑

数据库大小写敏感是一个影响比较大的一个坑,笔者的项目再前期对此问题认识不足,导致后续迁移改造过程中问题不断。(╯‵□′)╯︵┻━┻

如果旧数据库的库表与字段名不规范的话,会导致很多情况下无法正常的查询和获取结果列。

数据库大小写敏感后,使用小写且未用双引号括起的查询语句,会被数据库自动转为大写,如果库中表名字段名为小写时,无法完成查询,会提示找不到对象;

当数据库表与字段名为大写时,可以完成查询,但是返回的数据字段名均为大写,

如果使用map接收返回数据并在后续代码中使用get(字段名)获取时,则会无法获取到,产生错误;

在使用MyBait-plus的情况下,某些关键字被加上双引号进行查询,会导致达梦报错;

达梦的官方文档中也提到此问题:

https://eco.dameng.com/document/dm/zh-cn/faq/faq-mysql-dm8-migrate.html#快速设置使得在 mybatis 下对象不用添加双引号

函数相关

部分MySQL常用函数在达梦数据库中缺失或与达梦的函数不是很一致。

下面是我整理的在迁移过程中遇到的

FIND_IN_SET

达梦内置函数没有mysql的find_in_set()函数,但是可以通过自定义函数实现,也是达梦官方推荐的结局方案

自定义一个 FIND_IN_SET 函数

CREATE OR REPLACE FUNCTION FIND_IN_SET
                (
                        piv_str1 varchar2,
                        piv_str2 varchar2,
                        p_sep    varchar2 := ',')
                RETURN NUMBER
                            IS
                l_idx     number:=0;                 -- 用于计算piv_str2中分隔符的位置
                str       varchar2(500);             -- 根据分隔符截取的子字符串
                piv_str   varchar2(500) := piv_str2; -- 将piv_str2赋值给piv_str
                res       number        :=0;         -- 返回结果
                loopIndex number        :=0;
        BEGIN
                -- 如果piv_str中没有分割符,直接判断piv_str1和piv_str是否相等,相等 res=1
                IF instr(piv_str, p_sep, 1) = 0 THEN
                        IF piv_str          = piv_str1 THEN
                                res        := 1;
                        END IF;
                ELSE
                        -- 循环按分隔符截取piv_str
                        LOOP
                                l_idx    := instr(piv_str, p_sep);
                                loopIndex:=loopIndex+1;
                                -- 当piv_str中还有分隔符时
                                IF l_idx > 0 THEN
                                        -- 截取第一个分隔符前的字段str
                                        str:= substr(piv_str, 1, l_idx-1);
                                        -- 判断 str 和piv_str1 是否相等,相等 res=1 并结束循环判断
                                        IF str      = piv_str1 THEN
                                                res:= loopIndex;
                                                EXIT;
                                        END IF;
                                        piv_str := substr(piv_str, l_idx+length(p_sep));
                                ELSE
                                        -- 当截取后的piv_str 中不存在分割符时,判断piv_str和piv_str1是否相等,相等 res=1
                                        IF piv_str  = piv_str1 THEN
                                                res:= loopIndex;
                                        END IF;
                                        -- 无论最后是否相等,都跳出循环
                                        EXIT;
                                END IF;
                        END LOOP;
                        -- 结束循环
                END IF;
                -- 返回res
                RETURN res;
        END FIND_IN_SET;

GROUP_CONCAT

wm_concat()

LISTAGG (exp1, spr)WITHIN GROUP(<ORDERBY 项>)

原MySQL:

GROUP_CONCAT(b.opt ORDER BY q.order_number ASC)

新:

LISTAGG(b.opt) WITHIN GROUP (ORDER BY q.order_number ASC) AS opt

如果在GROUP_CONCAT()函数中存在distinct操作的话,需要重新改写SQL语句提前去重 或者使用wm_concat()

concat_ws (双引号分隔符"-")

达梦不能用双引号括起分隔符,会被认为是库表

原MySQL:

CONCAT_WS("-",j.starting_city,j.round_route,j.return_city) 

新:

CONCAT_WS('-',j.starting_city,j.round_route,j.return_city) 

date_add

dateadd(day,a2.leave_days + 30,a2.sjcfTime) 单位,n,date

原MySQL:

date_add(a2.sjcfTime, interval a2.leave_days+30 day)

新:

dateadd(day,a2.leave_days  + 30,a2.sjcfTime)

拼音排序

可以使用 NLSSORT 函数实现此功能,NLSSORT 支持以拼音、笔画、部首排序,示例如下:

拼音:SELECT * FROM PEOPLE ORDER BY NLSSORT(name,'NLS_SORT = SCHINESE_PINYIN_M');
笔划:SELECT * FROM PEOPLE ORDER BY NLSSORT(name,'NLS_SORT = SCHINESE_STROKE_M');
部首:SELECT * FROM PEOPLE ORDER BY NLSSORT(name,'NLS_SORT = SCHINESE_RADICAL_M');

last_insert_id()

可以查询自增列当前值、种子和增量等信息:

SELECT IDENT_CURRENT(‘SYSDBA.IDENT_TABLE’);

SELECT IDENT_SEED(‘SYSDBA.IDENT_TABLE’);

SELECT IDENT_INCR(‘SYSDBA.IDENT_TABLE’);

参考《DM8系统管理员手册》9.6.2 章节:查看自增列信息(手册在DM安装目录下的doc目录下)

now()

MySQL中now()查询当前时间

达梦中为CURRENT_DATE()返回当前日期

CURTIME(n) CURRENT_TIME(n)

SELECT CURDATE();
SELECT CURRENT_TIME() 

DATEDIFF()

返回时间间隔DATEDIFF()

mysql:

datediff(now(),date_add(day,a.stay_days + 30,a.start_time))

dm:

DATEDIFF(datepart,date1,date2)
datediff(day,dateadd(day,a.stay_days + 30,a.start_time),CURRENT_DATE())

datepart取值:year month day hour minute second

datediff日期计算顺序与mysql相反!!为date2-date1!

DATE_FORMAT()

达梦数据库中,使用'%Y年%m月'去格式化日期的时候,会报错

mysql:

DATE_FORMAT(a.last_time,'%Y年%m月')

dm:

CONCAT_WS('',DATE_FORMAT( a.last_time, '%Y' ) ,'年' ,  DATE_FORMAT( a.last_time, '%m' ) , '月' )

date_part(a.start_time, 'yyyy') || '年' || date_part(a.start_time, 'mm') || '月' || date_part(a.start_time, 'dd') || '日'

银河麒麟系统上,DATE_FORMAT(a.last_time,'%Y-%m-%d')会导致格式化后的日期附带有‘~’号

要改为使用to_char()函数

CHAR类型字段问题

最好在迁移的时候将char类型直接映射为varchar类型