目录

一、背景

二、历程

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库的所有脚本如下:

mysql表结构转postgresql工具 mysql表转oracle_oracle

 其中一份sql文件代表一个单独的库,对应到oracle数据库中,即一个用户,创建好oracle的表空间、用户。

2、工具选择

经过一番比对,最终还是选择我们熟悉的Navicat。

3、表结构迁移(先解决可能存在的问题)

迁移的过程中,遇到了很多问题,走了很多弯路,下面给大家梳理出一条能够直达的路。

先将不适配oracle的表结构修改好,再导出。

1)命名长度超长

直接运行mysql转为oracle的脚本时,可能会遇到如下问题:

mysql表结构转postgresql工具 mysql表转oracle_oracle_02

此报错提示“标识符”过长,查询资料得知,Oracle数据库兼容级别在12.2以前,对象名长度只能低于31个字符, 在12.2以上,可以达到128个字符。可能是表名、列名、索引名等过长导致,我们为了避免此问题,得规范命名,修改超长的表名、列名、索引名。

2)命名重复

mysql表结构转postgresql工具 mysql表转oracle_oracle_03

在mysql中,同一个库不同的表中,索引名称是可以重复的;但是在oracle中,一个SCHEMA下的对象是不能用相同的名字命名的,因此索引的名称不能重复。建议统一索引命名规范。

3)列重复创建索引

mysql表结构转postgresql工具 mysql表转oracle_oracle_04

mysql中,主键列还可以单独创建索引(虽然没有什么意义),但是oracle中不可以在列上重复创建索引,建议删除无效的重复列索引。

4)创建唯一索引失败

以下类型的唯一索引在oracle中无法创建,mysql的null做比较,既不是等于,也不是不等,比较特别,所以mysql不会有问题,但是在oracle中会用问题,需要将唯一索引改为普通索引

mysql表结构转postgresql工具 mysql表转oracle_oracle_05

mysql表结构转postgresql工具 mysql表转oracle_oracle_06

5)模型转换

①选择mysql库,右击-逆向数据库到模型

mysql表结构转postgresql工具 mysql表转oracle_数据库_07

②选择 转换模型为...

mysql表结构转postgresql工具 mysql表转oracle_mysql_08

③选择对应的数据库版本

mysql表结构转postgresql工具 mysql表转oracle_oracle_09

④选择 导出sql

mysql表结构转postgresql工具 mysql表转oracle_数据库_10

⑤点击确定保存表结构sql

mysql表结构转postgresql工具 mysql表转oracle_数据库_11

6)脚本大小写处理

导出的oracle的建表语句中,使用了双引号,会使创建出的表名、字段名为小写,会影响程序中的sql操作,需要删除所有的双引号。

7)字段类型修改

有些版本的navicat会将mysql的char、varchar类型转换为nchar和nvarchar2,这种情况需要将nchar和nvarchar2改为char和varchar2。

8)varchar2类型长度不够问题

oracle的varchar2类型有两种单位,分别为BYTE和CHAR,默认单位为BYTE;

mysql表结构转postgresql工具 mysql表转oracle_sql_12

但是在mysql中,4.1及之后的版本,VARCHAR的单位是字符;4.1之前的版本,VARCHAR的单位是字节。如果原本在mysql中是以字符为单位,那么到oracle中,以字节为单位再去存储原本的数据,就会发生存不下的情况,因此需要修改varchar2的单位为CHAR。

在脚本开头加入如下sql即可:

alter session set NLS_LENGTH_SEMANTICS=CHAR;

9)运行脚本

去对应的模式下运行对应的库脚本,至此,oracle的表就创建成功了。

4、数据传输

表结构创建好后,开始迁移数据,数据的迁移有两种方式,一种是直接传输,还有一种是导出数据脚本,下面分别介绍下两种方式。

1)使用工具

①选择 工具-数据传输

mysql表结构转postgresql工具 mysql表转oracle_oracle_13

②选择好对应的数据源、数据库

mysql表结构转postgresql工具 mysql表转oracle_数据库_14

③点击选项,勾选转换对象名为大写,点击下一步

mysql表结构转postgresql工具 mysql表转oracle_oracle_15

 ④点击开始,等待任务完成即可

mysql表结构转postgresql工具 mysql表转oracle_sql_16

2)使用sql脚本导入

①选择 工具-数据传输

mysql表结构转postgresql工具 mysql表转oracle_oracle_13

②目标选择文件,SQL格式选择对应的数据库版本

mysql表结构转postgresql工具 mysql表转oracle_mysql_18

③配置选项如下,点击下一步

mysql表结构转postgresql工具 mysql表转oracle_oracle_19

④选择需要导出的表,点击开始,等待任务完成即可

mysql表结构转postgresql工具 mysql表转oracle_sql_20

⑥oracle无法一次执行超长insert语句,需要对sql进行处理,如下:

insert into TABLE (ID, CONTENT) values (1, "超长内容... ...");

此sql执行会出现下面的报错:

mysql表结构转postgresql工具 mysql表转oracle_oracle_21

此时需要将超长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;

感谢阅读!