oracle向mysql改造


前言

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

函数式报错

start with connect by prior函数错误

问题描述:

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

解决方案:

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

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

create function getTaskObject    --getTaskObject 函数名称
(rootId varchar(100))             --输入值类型        
returns varchar(1024)            --返回值类型
as
    begin
        DECLARE sTemp VARCHAR(1024);   --定义变量
        DECLARE sTempChd VARCHAR(1024);--定义变量

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

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

​ 调用可以向平时调用内置函数一样调用,这个函数的返回值是所有符合条件id拼接起来字符串,所以我们调用也得加find_in_set(id,getTaskObject(值))

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

create function getTaskObject    --getTaskObject 函数名称
(rootId varchar(100))             --输入值类型        
returns varchar(1024)            --返回值类型
as
    BEGIN 
        DECLARE sTemp VARCHAR(100); --定义变量
        DECLARE sTempChd VARCHAR(100);--定义变量
        DECLARE allData VARCHAR(1024);--定义变量
        SET allData='';                
        SET sTempChd =cast(rootId as CHAR);
        SET sTemp='';
        WHILE sTempChd IS NOT NULL DO --如果不空一直循环
                SET sTemp=sTempChd;
                SET allData=CONCAT(allData,',',sTempChd);
                SELECT PARENT_ID INTO sTempChd FROM usbp_task_object where OBJECT_ID=sTemp;
        END WHILE;  --结束循环
        RETURN allData;     --返回结果
    END

延伸:

传入的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)。

测试存储过程

​ 以上是在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();
调用报方法read-Only错误

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

Sysdate报错

​ 解决Sysdate替换为sysdate()

to_char报错

​ 网上查找的资料:

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

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

日期的计算

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

近一个月:

​ Oracle——–>sysdate-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 &lt;= 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>

Rownum报错

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

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

||运算符报错

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

其他报错

表别名报错

别名报错,首先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=?

Mysql没有序列问题

存储类型bigInteger装换toBigDecimal

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

这是详情

union报错

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


文章作者: XuKun Chen
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 XuKun Chen !
评论
 上一篇
HTTP详解 HTTP详解
面试官,别问我 HTTP 了!看这 30 张图就行!PS:以下文章来源于公众号小林coding ,作者小林coding,本人觉得不错就先收藏起来。 前言在面试过程中,HTTP 被提问的概率还是比较高的。 这里搜集了 5 大类 HTTP 面试
本篇 
oracle向mysql改造 oracle向mysql改造
前言​ 由于工作需要,把oracle数据库改造成mysql数据库,而oracle和mysql在一定程度上有很大的区别,我们的工作是在已有项目改bug,所以特地总结一下这几天项目需要的改动。 函数式报错start with
  目录