目录
一、背景
二、历程
1、数据库准备
2、工具选择
3、表结构迁移(先解决可能存在的问题)
1)命名长度超长
2)命名重复
3)列重复创建索引
4)创建唯一索引失败
5)模型转换
6)脚本大小写处理
7)字段类型修改
8)varchar2类型长度不够问题
9)运行脚本
4、数据传输
1)使用工具
2)使用sql脚本导入
5、语法对比
1)函数差异
2)分页差异
3)连接查询
4)引号的识别
5)递归查询
6)插入、删除
一、背景
最近接到个任务,要求将Mysql数据库的数据迁移至Oracle数据库,期间遇到了不少问题,在此做一个记录、总结与分享。
二、历程
1、数据库准备
mysql库的所有脚本如下:
其中一份sql文件代表一个单独的库,对应到oracle数据库中,即一个用户,创建好oracle的表空间、用户。
2、工具选择
经过一番比对,最终还是选择我们熟悉的Navicat。
3、表结构迁移(先解决可能存在的问题)
迁移的过程中,遇到了很多问题,走了很多弯路,下面给大家梳理出一条能够直达的路。
先将不适配oracle的表结构修改好,再导出。
1)命名长度超长
直接运行mysql转为oracle的脚本时,可能会遇到如下问题:
此报错提示“标识符”过长,查询资料得知,Oracle数据库兼容级别在12.2以前,对象名长度只能低于31个字符, 在12.2以上,可以达到128个字符。可能是表名、列名、索引名等过长导致,我们为了避免此问题,得规范命名,修改超长的表名、列名、索引名。
2)命名重复
在mysql中,同一个库不同的表中,索引名称是可以重复的;但是在oracle中,一个SCHEMA下的对象是不能用相同的名字命名的,因此索引的名称不能重复。建议统一索引命名规范。
3)列重复创建索引
mysql中,主键列还可以单独创建索引(虽然没有什么意义),但是oracle中不可以在列上重复创建索引,建议删除无效的重复列索引。
4)创建唯一索引失败
以下类型的唯一索引在oracle中无法创建,mysql的null做比较,既不是等于,也不是不等,比较特别,所以mysql不会有问题,但是在oracle中会用问题,需要将唯一索引改为普通索引
5)模型转换
①选择mysql库,右击-逆向数据库到模型
②选择 转换模型为...
③选择对应的数据库版本
④选择 导出sql
⑤点击确定保存表结构sql
6)脚本大小写处理
导出的oracle的建表语句中,使用了双引号,会使创建出的表名、字段名为小写,会影响程序中的sql操作,需要删除所有的双引号。
7)字段类型修改
有些版本的navicat会将mysql的char、varchar类型转换为nchar和nvarchar2,这种情况需要将nchar和nvarchar2改为char和varchar2。
8)varchar2类型长度不够问题
oracle的varchar2类型有两种单位,分别为BYTE和CHAR,默认单位为BYTE;
但是在mysql中,4.1及之后的版本,VARCHAR的单位是字符;4.1之前的版本,VARCHAR的单位是字节。如果原本在mysql中是以字符为单位,那么到oracle中,以字节为单位再去存储原本的数据,就会发生存不下的情况,因此需要修改varchar2的单位为CHAR。
在脚本开头加入如下sql即可:
alter session set NLS_LENGTH_SEMANTICS=CHAR;
9)运行脚本
去对应的模式下运行对应的库脚本,至此,oracle的表就创建成功了。
4、数据传输
表结构创建好后,开始迁移数据,数据的迁移有两种方式,一种是直接传输,还有一种是导出数据脚本,下面分别介绍下两种方式。
1)使用工具
①选择 工具-数据传输
②选择好对应的数据源、数据库
③点击选项,勾选转换对象名为大写,点击下一步
④点击开始,等待任务完成即可
2)使用sql脚本导入
①选择 工具-数据传输
②目标选择文件,SQL格式选择对应的数据库版本
③配置选项如下,点击下一步
④选择需要导出的表,点击开始,等待任务完成即可
⑥oracle无法一次执行超长insert语句,需要对sql进行处理,如下:
insert into TABLE (ID, CONTENT) values (1, "超长内容... ...");
此sql执行会出现下面的报错:
此时需要将超长insert语句转换成一条insert语句+多条update语句,如下:
INSERT INTO table VALUES (1, '超长内容第一部分');
update table set content = content || '超长内容第二部分';
update table set content = content || '超长内容第三部分';
update table set content = content || '超长内容第四部分';
...
...
update table set content = content || '超长内容最后部分';
此时就可以成功执行。
⑦修改好sql后,获得全量的数据脚本,再去对应的模式下运行sql,就可以将数据导入。
5、语法对比
mysql与oracle中sql语法存在差异,部分代码需要做调整,下面列出可能影响的点。
1)函数差异
Mysql | Oracle | 说明 |
字符串拼接 | ||
concat('a','b') | 'a'||'b' | 拼接字符串 |
时间函数 | ||
NOW() | sysdate | 获取当前日期 |
date_format(NOW(),'%Y-%m-%d') | to_char(sysdate, 'YYYY-MM-DD') | 时间转换为字符串 |
str_to_date('2015-02-25','%Y-%m-%d') | to_date('2020-12-01', 'YYYY-MM-DD') | 字符串转换为时间 |
条件函数 | ||
ifnull(tab.columnName, 0) | nvl(tab.columnName, 0) | 如果tab.columnName值为空,则返回值取0,否则取tab.columnName |
if(expr1,expr2,expr3) | nvl2(expr1,expr2,expr3) | 如果expr1不为null,则返回expr2,否则返回expr3 |
if(value=val1, val2, val3) | DECODE(value, val1, val2, val3) | 如果value等于val1,则返回val2,否则返回val3 |
case when value=if1 then val1 when value=if2 then val2...when value=ifn then valn else val end; | DECODE(value, if1, val1, if2,val2,...,ifn, valn, val) | 如果value等于if1,则返回val1,如果value等于if2,则返回value2...如果value等于ifn,则返回valn,否则返回val |
行列转换函数 | ||
group_concat(…) | to_char(wm_concat(…)) | 行列转换 |
listagg(…) within group (order by …) | ||
类型转换 | ||
cast(123 AS CHAR(3)) | to_char(123) | 将数字123转换为字符串123 |
cast('123' as SIGNED) | to_number('123') | 将字符串数字123转换为数字类型 |
trunc()函数 | ||
truncate(12.123, 0) | TRUNC(12.123) | 返回整数12 |
truncate(12.123, 2) | TRUNC(12.123, 2) | 返回值保留2位小数12.12 |
json操作 | ||
... ... |
2)分页差异
-- mysql-limit语法:
SELECT * from student limit 5; -- 查询前5条数据
SELECT * from student limit 5, 8; -- 从第5(序号从0开始)条开始,查8条记录
-- oracle-rownum语法:
SELECT * FROM STUDENT WHERE ROWNUM = 1; -- 查询第一条数据
SELECT * FROM STUDENT WHERE ROWNUM <= 10; -- 获取前10条数据
-- 但rownum不支持查询后几条或第n(n>1)条数据,例如以下sql是不支持的
SELECT * FROM STUDENT WHERE ROWNUM > 2;
SELECT * FROM STUDENT WHERE ROWNUM = 3;
3)连接查询
-- MySQL使用join来完成连接查询
-- MySQL 左关联
select * from studenta left join studentb on studenta.id=studentb.id;
-- MySQL 右关联
select * from studenta right join studentb on studenta.id=studentb.id;
-- Oracle使用(+)来完成连接查询
-- Oracle 左关联
select * from studenta, studentb where studenta.id = studentb.id(+);
-- Oracle 右关联
select * from studenta, studentb where studenta.id(+) = studentb.id;
4)引号的识别
MySQL可识别双引号和单引号,Oracle只能识别单引号。
5)递归查询
mysql不支持递归查询,但是oracle支持,如下:
select A.org_id from org A
start with
A.org_id = '1234'
connect by prior
A.org_id =A.super_org_id
6)插入、删除
-- mysql
replace into ... ;
insert into ... select ... ;
DELETE FROM student WHERE id = 123;
--oracle
merge into ... ;
DELETE FROM student A WHERE A.id = 123;
DELETE student WHERE id = 123;
感谢阅读!