title: oracle向mysql改造
abbrlink: 23712
date: 2020-1-10 11:44:53
tags:

  • oracle
  • mysql
    categories:
  • oracle
  • mysql

前言

      由于工作需要,把oracle数据库改造成mysql数据库,而oracle和mysql在一定程度上有很大的区别,我们的工作是在已有项目改bug,所以特地总结一下这几天项目需要的改动。

1.函数式报错

1.1start with connect by prior函数错误

1.1.1问题描述:

      这个是oracle特有的一个函数,是根据条件递归查询"树"。常用用法:start with connect by prior parentId=id或start with connect by prior id=parentId,第一种是:查询结果自己所有的后代节点(包括自己);第二种用法是查询结果自己所有的父类节点(包括自己)。

1.1.2解决方案:

      Mysql不支持通过start with connect by进行递归查询,所以创建函数进行查找。

      首先是第一种:查询结果自己所有的后代节点(包括自己),首先得了解find_in_set这个函数作用,举个例子:find_in_set(parent_id,”1,2,3”)的意思是parent_id在1,2,3内。创建函数如下:

set global log_bin_trust_function_creators=TRUE;#创建前需要
create function getTaskObjects    #getTaskObject 函数名称
(rootId varchar(100))			 #输入值类型		
returns varchar(1024)            #返回值类型
BEGIN
		DECLARE sTemp VARCHAR(1024);   #定义变量
		DECLARE sTempChd VARCHAR(1024);#定义变量

		SET sTemp = '';
		-- SET sTempChd = CAST(auth_id AS CHAR);
		SET sTempChd = rootId;

		WHILE sTempChd IS NOT NULL DO #如果不空一直循环
			IF sTemp = '' THEN
				SET sTemp = sTempChd;
			ELSE
				SET sTemp= CONCAT(sTemp,',',sTempChd);
			END IF;
			SELECT GROUP_CONCAT(dept_id) INTO sTempChd FROM sys_dept WHERE FIND_IN_SET(parent_id,sTempChd)>0;#把所有的id拼接接起来
		END WHILE;#结束循环
		RETURN sTemp; #返回结果
END

      调用可以向平时调用内置函数一样调用,以下面这张表为例,这是全部数据。

orthanc改成MySQL oracle改造mysql_mysql


两个查找和与之对应结果:

select getTaskObjects('101')
select * from sys_dept  WHERE find_in_set(dept_id,getTaskObjects('101'))>0

orthanc改成MySQL oracle改造mysql_数据库_02


orthanc改成MySQL oracle改造mysql_mysql_03


      第二种:查询结果自己所有的父类节点,返回值也是所有符合条件id拼接起来字符串,所以我们调用也得加find_in_set(id,getTaskObject(子节点id))

create function getTaskObjects2    #getTaskObject 函数名称
(rootId varchar(100))			 #输入值类型		
returns varchar(1024)            #返回值类型
BEGIN 
		DECLARE sTemp VARCHAR(100); #定义变量
		DECLARE sTempChd VARCHAR(100);#定义变量
		DECLARE allData VARCHAR(1024);#定义变量
		SET allData='';				
		SET sTempChd =rootId;
		SET sTemp='';
		WHILE sTempChd !=0 DO #如果不空一直循环
				SET sTemp=sTempChd;
				SET allData=CONCAT(allData,',',sTempChd);
				SELECT PARENT_ID INTO sTempChd FROM sys_dept where dept_id=sTemp;
		END WHILE;  #结束循环
		RETURN allData;     #返回结果
	END

      一样以下面这张表为例,这是全部数据。

orthanc改成MySQL oracle改造mysql_mysql


两个查找和与之对应结果:

select getTaskObjects2('101')
select * from sys_dept  WHERE find_in_set(dept_id,getTaskObjects2('101'))>0

orthanc改成MySQL oracle改造mysql_orthanc改成MySQL_05


orthanc改成MySQL oracle改造mysql_sql_06

1.1.3 延伸:

1.1.3.1 传入的table为动态table表怎么办?

      本以为这样写就可以全部用上,但这个有个明显的不足就是table只能写死,一开始的想法是把table传进去,但报错了,我是这样写的:

Select parent_id into sTempChd from tableName(ps:这是个变量) where object_id=sTemp;

      会报tableName这个表不存在的错误,因为sql不会自动识别tableName这个变量,它会认为有这个tableName的表 。

      网上查找了资料说可以用动态sql,但函数不支持动态sql,所以解决办法就是写一个存储过程,以下讲解的是第二种查询结果自己所有的父类节点,如下:

CREATE PROCEDURE SELECT_TREE(IN keyColumn VARCHAR(32) CHARSET utf8,IN tableName VARCHAR(32) CHARSET utf8,IN parentColumn VARCHAR(32) CHARSET utf8,
IN keyValue VARCHAR(64) CHARSET utf8,OUT returndata VARCHAR(2048) CHARSET utf8)--输入输出参数,in为输入,out为输出
	BEGIN
			declare sTemp VARCHAR(100); --定义变量
			SET sTemp = keyValue;
			SET returndata='';
				
			WHILE sTemp IS NOT NULL DO--如果不空一直循环
					-- 创建sql
					SET @selectSql = CONCAT('select ',parentColumn,' INTO @recordcount  from ',tableName,' where ',keyColumn,"='",sTemp,"'");
					-- 执行动态生成的sql语句
					PREPARE temp FROM @selectSql;
					EXECUTE temp;
					SET sTemp = @recordcount;--其实对比上面仔细看可以看出只是这一步不同
					IF sTemp IS NOT NULL THEN
					SET returndata=CONCAT(returndata,',',sTemp);
					END IF;
			END WHILE; --结束循环
	END

      可以对比上面的自定义函数,一个注意地方是@recordcount 代替sTempChd 变量,声明这是个变量,其他是语法问题,逻辑和自定义函数相同。

       然后就是调用测试,我是在Navicat测试的,我的版本可以直接传参(听说低版本不可以直接测试)。注意的是out输出参数要传递变量,如:(keycolum,tableName,pid,in,@returnData)。以项目中一张表为例:

orthanc改成MySQL oracle改造mysql_mysql_07

call SELECT_TREE('package_id','usbp_object_package','parent_id','74',@returnData);
SELECT @returnData

       运行结果:

orthanc改成MySQL oracle改造mysql_mysql_08

       以上是在Navicat调用的,在项目中,我们需要写sql调用,用的是mybatis框架,调用如下,最重要是要加上statementType=”CALLABLE”,如果传入是map,returnData这个名字可以随便取,传入是pojo,这个值必须固定,在pojo里面应该有个同名变量。返回值去传入的参数里拿,这个我传入的是map,所以直接去传入的map拿了,至于传入pojo要给个变量returnData,会自动注入。


<mapper namespace="com.shd.biz.Tree.service.impl.TreeServiceImpl">
    <!-- 注意 statementType="CALLABLE" -->
	<select id="treeParent" statementType="CALLABLE">
		{call SELECT_TREE(
		#{keyColumn,mode=IN},#{tableName,mode=IN},#{parentColumn,mode=IN},#{keyValue,mode=IN}
		,#{returnData,mode=OUT,jdbcType=VARCHAR})}
	</select>
</mapper>


Map<String, Object> parameters=new HashMap<String, Object>();
		parameters.put("keyValue", value);
		parameters.put("tableName", table);
		parameters.put("keyColumn", keyColumn);
		parameters.put("parentColumn", parentColumn);
		this.getDao().queryForDataSet(SQL_PRIFIX + "treeParent", parameters);
		//结果是从传入参数里面拿,不是从返回结果拿
		String returnData = parameters.get("returnData").toString();
1.1.3.2 调用报方法read-Only错误

       最后还有个报错是事务报错,是因为我项目get开头的方法设置了readOnly=“true”,而调用存储过程如果还设置readOnly属性就会报错,要加上@Transactional(readOnly = false)或改变service方法名。

1.2 Sysdate报错

       解决Sysdate替换为sysdate()

1.3 to_char报错

      网上查找的资料:

      date_format(date,’%Y-%m-%d’) -------------->oracle中的to_char();

      str_to_date(date,’%Y-%m-%d’) -------------->oracle中的to_date();


      具体还得看链接

1.4 日期的计算

      oracle 可以直接用sysdate加减乘除,而mysql不可以,如

      近一个月:

      Oracle-------->sysdate-29//日期,今天减去29天

       Mysql--------->

<if test="REPORT_TIME == '本周'">
				and YEARWEEK(date_format(report_time,'%Y-%m-%d')) = YEARWEEK(now()) 
			</if>
			<if test="REPORT_TIME == '本月'">
				and report_time >=DATE_ADD(curdate(),interval -day(curdate())+1 day) and report_time <= last_day(SYSDATE())
			</if>
			<if test="REPORT_TIME == '近1个月'">
				and report_time BETWEEN date_sub(SYSDATE(),interval 1 month) AND SYSDATE()
			</if>
			<if test="REPORT_TIME == '近3个月'">
				and report_time BETWEEN date_sub(SYSDATE(),interval 3 month) AND SYSDATE()
			</if>
			<if test="REPORT_TIME == '近1年'">
				and report_time BETWEEN date_sub(SYSDATE(),interval 12 month) AND SYSDATE()
			</if>

1.5 Rownum报错

      在mysql中没有Rownum,所以要特殊的实现方法,实现和效果如下,但我项目是用来分页,所以直接就用limit。


SELECT @rownum:=@rownum+1 AS rownum, table .*
FROM (SELECT @rownum:=0) r, table ;

1.6 ||运算符报错

       Mysql中||表示或的意识,而Oracle中||运算符可以将两个或两个以上的字符串连接在一起,在mysql中用concat代替。

2 其他报错

2.1 表别名报错

       别名报错,首先mysql不可以像Oracle一样直接多重查找不加别名,如:

Select key from(select key from database);

改为Select t.key from(select key from database) t;

       但在删除时又不可以加别名,如 :

Delete from database r where r.id=?会报错,得改为:

Delete from database where id=?

2.2 Mysql没有序列问题

       重新解决的办法几个,一个是用uid,另外一个也是根据自定义函数等进行创建一个伪序列,具体创建有一篇博客已经详细介绍了,

2.3 存储类型bigInteger装换toBigDecimal

       原来oracle存储数据类型是integer,现在存储类型为decimal,启动时报错,bigInteger cannot to cast toBigDecimal,所以,就得装换一下。


2.4 union报错

       Oracle的union和mysql的union的一个细节错误导致,oracle可以用括号括起来,而mysql不可以