一套sql语句同时支持Oracle跟Mysql?

最近项目在做一个需求:要求书写mpper.xml里面的sql语句同时兼容Oracle数据库跟mysql数据库的支持
ORACLE和MYSQL作为两款使用最广泛的关系型数据库软件,在各项功能上以及编程语法上还是存在很大的差异的,因此要实现将系统从ORACLE迁移至MYSQL数据库上,数据的迁移仅仅是一方面,最大的挑战在于代码层面的改动,整个迁移的大致工作如下:
先给大家说一下思路:我们就是通过一套sql来支持对两个数据库语法的支持,采用动态切换连接数据源来判断连接的是oracle数据库还是mysq数据库。
配置文件及xml的部分事例:
一套sql语句同时支持Oracle跟Mysql?_第1张图片
一套sql语句同时支持Oracle跟Mysql?_第2张图片
1、ORACLE与MYSQL功能上的区别:
(1)字段类型的对比:

序号 ORACLE MYSQL
1 VARCHAR2 VARCHAR
2 DATE DATETIME
3 TIMESTAMP DATETIME
4 NUMBER DECIMAL
5 INTEGER DECIMAL(22,0)
6 CLOB TEXT
7 BLOB LONGBLOB

(2)常用功能语法上的对比:

序号 对比项 ORACLE MYSQL
1 空字符的判断 NAME IS NULL NAME=’ ’
2 from select 1 from dual; from后边接表名 select 1; from不是必须的,且无需接表名
3 like的用法 NAME like ‘a%’ 大小写敏感,只查询以小写字母a开头的字符串 NAME like ‘a%’ 大小写不敏感,查询以小写字母a或大写字母A开头的字符串
4 日期格式化 获取系统日期:SYSDATE() 格式化日期:TO_CHAR、TO_DATE函数 获取系统日期:now() 格式化日期:str_to_date、date_format函数
主键 一般通过Oracle序列生成: SEQ_TEST.NEXTVAL SEQ_TEST.CURRVAL Auto_increment属性实现自增获得唯一值
5 分页(常用写法) SELECT T2.* FROM (SELECT T1.*, ROWNUM RN FROM (SELECT * FROM TEST ORDER BY SID DESC) T1 WHERE ROWNUM < 5) T2 WHERE RN >= 1 SELECT * FROM TEST_TEST1 ORDER BY SID DESC LIMIT 0,4
6 字符串链接 SELECT sid || username|| PASSWORD FROM TEST SELECT concat(concat(sid , username), password) FROM TEST SELECT CONCAT(SID , USERNAME, PASSWORD) FROM TEST
7 分组函数 SELECT EMPNO,JOB, COUNT(1), SUM(SAL),SUM(COMM) FROM EMP; 以上SQL不能正常执行,select后面的列必须是分组的列或者是用了聚合函数的列 SELECT EMPNO,JOB, COUNT(1), SUM(SAL),SUM(COMM) FROM EMP; 随便分组都可以
8 分析函数 row_number() over (partition by xx order by xx) rank() over (partition by xx order by xx) dense_rank() over (partition by xx order by xx) count(1) over() Mysql不支持
9 并行 支持SQL级别并发处理 不支持

(3)数据库对象类型对比:

序号 数据库对象类型 ORACLE MYSQL 迁移方案
1 PROCEDURE (1)建存储过程用create procedure XXX 或 create or replace procedure XXX 两种语法 (2)存储过程参数不能指定精度或长度,如P_NAME VARCHAR2 (3)参数后必须要有IS或AS (4)存储过程没有参数时()必须省略 (5)变量定义在is和begin之间 (1) 建存储过程只能用create procedure XXX一种语法 (2) 存储过程参数必须指定精度或长度,如P_NAME VARCHAR(100) (3) 参数后不能有IS或AS (4) 存储过程没有参数时必须保留() (5) 变量定义在begin和end之间 由于ORACLE跟MYSQL编写存储过程的语法差异,需重新修改存储过程代码
2 TRIGGER (1)包含DML触发器、替代触发器(视图)、系统触发器(DDL语句/系统事件) (2)支持语句级触发器和行级触发器 (3)一个触发器允许定义多个事件 (1)只支持DML触发器 (2)只支持行级触发器 (3)一个触发器只允许定义一个事件 由于语法差异以及触发器功能实现的差异,需修改触发器代码
3 FUNCTION 支持 支持 设计到语法差距以及内置函数的差别需重新改写代码
4 PACKAGE 支持 不支持 需将PACKAGE使用存储过程替换
5 VIEW 支持 支持 如涉及到使用MYSQL不支持的内置函数需转换
6 Materialized view 支持 不支持 采用视图替换或者修改代码直接方案基表方式
7 JOB 支持比较完善 支持比较简单 按需修改
8 DBLINK 支持 可使用FEDERATED引擎实现dblink访问功能,需修改程序代码
9 SEQUENCE 支持 可使用MYSQL自增列实现序列功能,需修改表结构添加自增列
10 SYNONYMS 支持 需修改程序代码直接访问物理表
11 CURSOR (1)静态游标(隐式游标,显示游标)、ref游标 (2)支持loop循环、while循环、for循环 (3)支持记录变量 (4)支持bulk collection语法批量操作 (1)只支持静态游标 (2)支持loop循环、repeat循环、while循环 (3)不支持记录变量 (4)只能单条操作 由于功能上的差距,需修改CURSOR代码

2ORACLE数据迁移至MYSQL

(1)字段类型的调整:

数据迁移主要需要注意的地方在于字段类型支持的差异上,比如ORACLE常用的VARCHAR2类型以及CLOB类型在MYSQL中都不存在,因此我们需要稍微调整下字段类型,针对我们数据库目前的现状,有如下字段类型需要修改:

DATA_TYPE COUNT(*) 整改措施
VARCHAR2 3194 使用MYSQL VARCHAR类型替代
NUMBER 1522 使用MYSQL DECIMAL类型替代
DATE 776 使用MYSQL DATETIME类型替代
CHAR 544 无需整改
CLOB 89 使用MYSQL TEXT类型替代
NVARCHAR2 34 使用MYSQL VARCHAR类型替代
TIMESTAMP(6) 16 使用MYSQL DATETIME类型替代
BLOB 12 使用MYSQL LONGBLOB类型替代
FLOAT 6 无需整改
LONG RAW 4 使用MYSQL LONGTEXT类型替代
LONG 2 使用MYSQL LONGTEXT类型替代

(2)数据迁移:

目前MYSQL官方发布了一个将数据由SQL Server或Oracle中移植到MySQL中的工具包MySQLMigration Toolkit,该工具支持LOB字段数据的迁移,由于个创数据库目前的数据量相对较少,数据量目前统计将近17G左右,可以在停机的状态下使用该工具进行数据迁移,经过统计,目前个创数据库的表清单数量以及数据量分布如下:

OWNER 数量 数据量
QHIEX_PROD 357 6.7G
QHIEX 162 10G

(3)函数上的差异及一些常见的区别

编号 类别 ORACLE MYSQL 注释
1 数字函数 round(1.23456,4) round(1.23456,4) 一样: ORACLE:select round(1.23456,4) value from dual MYSQL:select round(1.23456,4) value
2 abs(-1) abs(-1) 功能: 将当前数据取绝对值 用法: oracle和mysql用法一样 mysql: select abs(-1) value oracle: select abs(-1) value from dual
3 ceil(-1.001)) ceiling(-1.001) 功能: 返回不小于 X 的最小整数 用法: mysqls: select ceiling(-1.001) value oracle: select ceil(-1.001) value from dual
4 floor(-1.001) floor(-1.001) 功能: 返回不大于 X 的最大整数值 用法: mysql: select floor(-1.001) value oracle: select floor(-1.001) value from dual
5 Max(expr)/Min(expr) Max(expr)/Min(expr) 功能:返回 expr 的最小或最大值。MIN() 和 MAX() 可以接受一个字符串参数;在这 种情况下,它们将返回最小或最大的字符串传下。 用法: ROACLE: select max(user_int_key) from sd_usr; MYSQL: select max(user_int_key) from sd_usr;
6 字符串函数 ascii(str) ascii(str) 功能:返回字符串 str 最左边的那个字符的 ASCII 码值。如果 str 是一个空字符串, 那么返回值为 0。如果 str 是一个 NULL,返回值也是 NULL. 用法: mysql:select ascii(‘a’) value oracle:select ascii(‘a’) value from dual
7 CHAR(N,…) CHAR(N,…) 功能:CHAR() 以整数类型解释参数,返回这个整数所代表的 ASCII 码值给出的字符 组成的字符串。NULL 值将被忽略. 用法: mysql:select char(97) value oracle:select chr(97) value from dual
8 REPLACE(str,from_str,to_str) REPLACE(str,from_str,to_str) 功能: 在字符串 str 中所有出现的字符串 from_str 均被 to_str 替换,然后返回这个字符串. 用法: mysql: SELECT REPLACE(‘abcdef’, ‘bcd’, ‘ijklmn’) value oracle: SELECT Replace(‘abcdef’, ‘bcd’, ‘ijklmn’) value from dual
9 INSTR(‘sdsq’,‘s’,2) INSTR(‘sdsq’,‘s’) 参数个数不同 ORACLE: select INSTR(‘sdsq’,‘s’,2) value from dual(要求从位置2开始) MYSQL: select INSTR(‘sdsq’,‘s’) value(从默认的位置1开始)
10 SUBSTR(‘abcd’,2,2) substring(‘abcd’,2,2) 函数名称不同: ORACLE: select substr(‘abcd’,2,2) value from dual MYSQL: select substring(‘abcd’,2,2) value
11 instr(‘abcdefg’,’ab’) locate(‘ab’,’abcdefg’) 函数名称不同: instr -> locate(注意:locate的子串和总串的位置要互换) ORACLE: SELECT instr(‘abcdefg’, ‘ab’) VALUE FROM DUAL MYSQL: SELECT locate(‘ab’, ‘abcdefg’) VALUE
12 length(str) char_length() 函数名称不同: ORACEL: SELECT length(‘AAAASDF’) VALUE FROM DUAL MYSQL: SELECT char_length(‘AAAASDF’) VALUE
13 REPLACE(‘abcdef’, ‘bcd’, ‘ijklmn’) REPLACE(‘abcdef’, ‘bcd’, ‘ijklmn’) 一样: ORACLE: SELECT REPLACE(‘abcdef’, ‘bcd’, ‘ijklmn’) value from dual MYSQL: SELECT REPLACE(‘abcdef’, ‘bcd’, ‘ijklmn’) value
14 LPAD(‘abcd’,14, ‘0’) LPAD(‘abcd’,14, ‘0’) 一样: ORACLE: select LPAD(‘abcd’,14, ‘0’) value from dual MYSQL: select LPAD(‘abcd’,14, ‘0’) value from dual
15 UPPER(iv_user_id) UPPER(iv_user_id) 一样: ORACLE: select UPPER(user_id) from sd_usr; MYSQL: select UPPER(user_id) from sd_usr;
16 LOWER(iv_user_id) LOWER(iv_user_id) 一样: ORACLE: select LOWER(user_id) from sd_usr; MYSQL: select LOWER(user_id) from sd_usr;
17 控制流函数 nvl(u.email_address, 10) IFNULL(u.email_address, 10) 或 ISNULL(u.email_address) 函数名称不同(根据不同的作用进行选择): ORACLE: select u.email_address, nvl(u.email_address, 10) value from sd_usr u (如果u.email_address=NULl,就在DB中用10替换其值) MYSQL: select u.email_address, IFNULL(u.email_address, 10) value from sd_usr u(如果u.email_address=NULl,显示结果中是10,而不是在DB中用10替换其值) select u.email_address, ISNULL(u.email_address) value from sd_usr u(如果u.email_address是NULL, 就显示1,否则就显示0)
18 DECODE(iv_sr_status,g_sr_status_com, ld_sys_date, NULL) 无,请用IF或CASE语句代替. IF语句格式:(expr1,expr2,expr3) 说明: 1. decode(条件,值1,翻译值1,值2,翻译值2,…值n,翻译值n,缺省值) 该函数的含义如下: IF 条件=值1 THEN     RETURN(翻译值1) ELSIF 条件=值2 THEN     RETURN(翻译值2)     … ELSIF 条件=值n THEN     RETURN(翻译值n) ELSE     RETURN(缺省值) END IF 2. mysql If语法说明 功能: 如果 expr1 是TRUE (expr1 <> 0 and expr1 <> NULL),则 IF()的返回值为expr2; 否则返回值则为 expr3。IF() 的返回值为数字值或字符串值,具体情况视其所在 语境而定。 用法: mysql: SELECT IF(1>2,2,3);
19 类型转换函数 TO_CHAR(SQLCODE) date_format/ time_format 函数名称不同 SQL> select to_char(sysdate,‘yyyy-mm-dd’) from dual; SQL> select to_char(sysdate,‘hh24-mi-ss’) from dual; mysql> select date_format(now(),’%Y-%m-%d’); mysql> select time_format(now(),’%H-%i-%S’);
20 to_date(str,format) STR_TO_DATE(str,format) 函数名称不同: ORACLE:SELECT to_date(‘2009-3-6’,‘yyyy-mm-dd’) VAULE FROM DUAL MYSQL: SELECT STR_TO_DATE(‘2004-03-01’, ‘%Y-%m-%d’) VAULE
21 trunc(-1.002) cast(-1.002 as SIGNED) 函数名称不同: TRUNC函数为指定元素而截去的日期值。 ORACLE: select trunc(-1.002) value from dual MYSQL:select cast(-1.002 as SIGNED) value MYSQL: 字符集转换 : CONVERT(xxx USING gb2312) 类型转换和SQL Server一样,就是类型参数有点点不同 : CAST(xxx AS 类型) , CONVERT(xxx,类型),类型必须用下列的类型: 可用的类型  二进制,同带binary前缀的效果 : BINARY 字符型,可带参数 : CHAR() 日期 : DATE 时间: TIME 日期时间型 : DATETIME 浮点数 : DECIMAL 整数 : SIGNED 无符号整数 : UNSIGNED
22 TO_NUMBER(str) CAST(“123” AS SIGNED INTEGER) 函数名称不同 ORACLE:SELECT TO_NUMBER(‘123’) AS VALUE FROM DUAL; MYSQL: SELECT CAST(“123” AS SIGNED INTEGER) as value; SIGNED INTEGER:带符号的整形
23 日期函数 SYSDATE now() / SYSDATE() 写法不同: ORACLE:select SYSDATE value from dual MYSQL:select now() value select sysdate() value
24 Next_day(sysdate,7) 自定义一个函数:F_COMMON_NEXT_DAY(date,int) 函数名称不同: ORACLE: SELECT Next_day(sysdate,7) value FROM DUAL MYSQL: SELECT F_COMMON_NEXT_DAY(SYSDATE(), 3) value from DUAL; (3:指星期的索引值)返回的指定的紧接着下一个星期的日期
25 ADD_MONTHS(sysdate, 2) DATE_ADD(sysdate(), interval 2 month) 函数名称不同: ORACLE: SELECT ADD_MONTHS(sysdate, 2) as value from DUAL; MYSQL: SELECT DATE_ADD(sysdate(), interval 2 month) as value from DUAL;
26 2个日期相减(D1-D2) DATEDIFF(date1,date2) 功能: 返回两个日期之间的天数。 用法: mysql: SELECT DATEDIFF(‘2008-12-30’,‘2008-12-29’) AS DiffDate oracle: 直接用两个日期相减(比如d1-d2=12.3)
27 SQL函数 SQLCODE MYSQL中没有对应的函数,但JAVA中SQLException。getErrorCode()函数可以获取错误号 Oracle内置函数SQLCODE和SQLERRM是特别用在OTHERS处理器中,分别用来返回Oracle的错误代码和错误消息。 MYSQL: 可以从JAVA中得到错误代码,错误状态和错误消息
28 SQLERRM MYSQL中没有对应的函数,但JAVA中SQLException。getMessage()函数可以获取错误消息 Oracle内置函数SQLCODE和SQLERRM是特别用在OTHERS处理器中,分别用来返回Oracle的错误代码和错误消息。 MYSQL: 可以从JAVA中得到错误代码,错误状态和错误消息
29 SEQ_BK_DTL_OPT_INT_KEY.NEXTVAL 自动增长列 在MYSQL中是自动增长列. 如下方法获取最新ID: START TRANSACTION; INSERT INTO user(username,password) VALUES (username,MD5(password)); SELECT LAST_INSERT_ID() INTO id; COMMIT;
30 SUM(enable_flag) SUM(enable_flag) 一样: ORCALE: SELECT SUM(enable_flag) FROM SD_USR; MYSQL: SELECT SUM(enable_flag) FROM SD_USR;
31 DBMS_OUTPUT.PUT_LINE(SQLCODE) 在MYSQL中无相应的方法,其作用是在控制台中打印,用于测试,对迁移无影响。 dbms_output.put_line每行只能显示255个字符,超过了就会报错
1、连接字符串在Oracle中用 ,SqlServer中用+,MySQL中用concat(‘a’,‘b’,‘c’)

2、orcale 生成唯一序列是 select sys.guid() from dual ,mysql是 select uuid() from dual

3、mysql可以实现自增长主键(通过字段的auto_increment属性);Oracle则需要通过序列(Sequence)来实现。

4、mysql可以用双引号来引用字符串(当然单引号也行);Oracle只能用单引号。

5、mysql在查询语句中可以通过limit [offset,] 来直接分页;而Oracle需要使用rownum伪列。

6、mysql对于真假的判断,0为假1为真;Oracle则是用true/false。

7、mysql的查询可以 select sysdate(); ;而Oracle需要引用虚表(select sysdate from dual;)。

8、mysql对于like的查询,CONCAT(’%’, #{name,jdbcType=VARCHAR},’%’) ;Oracle则是用LIKE ‘%’||#{name,jdbcType=VARCHAR}||’%’

9、mysql的查询可以 select sysdate(); ;而Oracle需要引用虚表(select sysdate from dual;)。

10、mysql中备份命令:mysqldump,执行结果是一个sql文件;oracle备份命令:dpdump,执行结果是一个dmp文件。前件是文本sql命令,

可以直接导入到其它mysql数据库,甚至可以稍作修改导入到其它类型的数据库;后者导出文件是二进制的,只能Oracle自己用(甚至还有版本限制)。

11、mysql中的命令默认是直接commit的;Oracle默认不是

12、mysql中日期的转换用dateformat()函数;Oracle用to_date()与to_char()两个函数。

13、mysql在Windows环境下大小写是不敏感的;unix/linux环境下,对数据库名、表名大小写敏感,列名大小写不敏感。Oracle则不论环境大小写都不敏感。

14、mysql支持枚举类型(enum)、集合类型(set);Oracle不直接支持,需要使用外键等其它手段实现。

15.Oracle中的decode在mysql中的等价实现

mysql支持if
格式:
IF(expr1,expr2,expr3)
如果expr1是TRUE(expr1<>;0且expr1<>;NULL),那么IF()返回expr2,否则它返回expr3。IF()返回一个数字或字符串值

oracle的写法
SELECT decode(ttype,1,’a',2,’b',3,’c',’d') FROM taba

可以在mysql里写成
SELECT if(ttype=1, 'a',if(ttype=2,'b', if(ttype =3, 'c', 'd'))) FROM taba

3系统测试(包括数据迁移的测试)

程序经过大量的修改后,测试以及BUG的修复往往是耗时最长也是最为重要的一环,各个功能都需要经过全面的测试以及验证工作,从而避免生出上线后出现的各种问题

你可能感兴趣的:(数据库,sql,数据库,sql)