五、SQL 转换:Oracle 转 MySQL

五、SQL 转换:Oracle 转 MySQL

一、需求

系统需要兼容 Oracle 和 MySQL 数据库,sql 需要转化(Oracle==>MySQL);
为此写了一个工具类进行 sql 的转化,该工具类可转换大部分的 sql。(仅供参考)。
注:因为可通过数据库连接判断数据库类型,工具类难以转化的 sql 可以写两套 SQL 来兼容


二、Oracle 转换 MySQL 之前需要了解的差异

两者之间的 SQL 转换,基本上是处理以上的差异。


三、处理思路
  • 1、使用栈结构对 sql 进行标准 sql 的拆分。
  • 2、对拆分后的标准 sql 进行差异处理(字符串处理)。
    • 处理左右连接
    • 处理 || 连接符
    • 处理 sql 语句中的函数差异
  • 3、组合处理后的标准sql 。
/*
1、如何将复杂 sql 分解为标准 sql 语句?
思路:
使用两个堆栈对象
Stack sqlStack = new Stack();
Stack selectStack = new Stack();
sqlStack 用来存放 sql 子句;selectStack 用来存放 sql 子句起始位置。

1、将 sql 字符串转换为字符数组。
2、循环处理字符串进行分析
 2-1、判断字符是否是 SQL 子句开始,是则入栈
*/

ConvertSql.java

package com.utils;

import java.util.*;
import java.util.regex.Pattern;

/**
 * Oracle 语句转换为 MySQL 可执行的语句的工具类
 */
public class ConvertSql {

    //SQL 不需要作转换
    private final static String _SQL_HEAD_NO_CONVERT = "--_NO_CONVERT_\r\n";
    //由 Oracle 转换之后得到的 MySQL
    private final static String _SQL_HEAD_CONVERTED_ORACLE_TO_MYSQL = "--_CONVERTED_ORACLE_TO_MYSQL_\r\n";

    private final static String _SQL_PLACEHOLDER = "__SQL__";//SQL子句占位符
    private static char[] spaceList = {' '};
    private static char[] spaceCharList = {
            ' ', ',', '>', '<', '=', '!',
            '+', '-', '*', '/'};

    private static String[] beginKeyList = new String[]{"SELECT", "UPDATE", "INSERT", "DELETE", "DROP"};


    public static String convertSqlToMySQL(String sql) {

        //1、判断 sql 是否需要转换、否:直接原格式返回
        if (!isNeedConvert(sql)) {
            return sql;
        }

        //2、进行 sql 转换
        sql = sql.trim().toUpperCase();//对 sql 首尾去空格后进行 大写转换
        if (sql.startsWith("UPDATE")) {
            sql = processUpdateOracleToMySQL(sql);
        }


        //3、替换外连接:左右连接。
        sql = convertLRRelation(sql);

        //4、 进行 || 字符串连接的替换: ||-->concat()函数
        if (sql.indexOf("||") > 0) {
            sql = convertStringRelationBreakDownSql(sql);
        }

        return sql;
    }

    /**
     * 分解sql为标准sql
     * @param SQL
     * @return
     */
    private static String convertStringRelationBreakDownSql(String SQL) {
        // 0 准备工作
        //SQL = SQL.toUpperCase();
        SQL = "(" + SQL + ")";
        // SQL子句堆栈
        Stack sqlStack = new Stack();
        // SQL子句位置堆栈
        Stack selectStack = new Stack();
        // 左括句位置堆栈
        Stack braceStack = new Stack();
        int count=0;
        int  temp;
        // 1 将字符串转换为字符数组
        char[] array = SQL.toCharArray();
        // 2 分析
        for (int i = 0; i < array.length; i++) {
            // 2.1 判断当前位置是否SQL子句开始,是则入栈
            if (isSQLStart(array, i)) {
                selectStack.push(new Integer(i));
            }
            //遇到左括号,则左括号入栈
            if (array[i] == '(') {
                braceStack.push(new Integer(i));
            }
            //遇到右括号,则左括号出栈
            if (array[i] == ')') {
                temp=((Integer)braceStack.pop()).intValue();
                //如果出栈位置==SQL子句开始位置,则认为该SQL子句结束
                if(temp==((Integer)selectStack.peek()).intValue()){
                    //select子句出栈
                    selectStack.pop();
                    //处理SQL子句,并将SQL子句入栈
                    SQL=convert(sqlStack,SQL,temp,i,count);
                    count++;
                }
            }
        }

        // 3 处理SQL子句的|| 连接符,并组装SQL
        int i=1;
        int subSQLLen;
        String subSQL;
        String parentSQL=SQL;
        while(!sqlStack.empty()){
            subSQL=(String)sqlStack.pop();
            subSQLLen=subSQL.length();
            // 3.1 调用原来的左右连接转换方法
            subSQL=convertStringRelation(subSQL);
            // 3.2 组装SQL
            parentSQL=insertSubSQL2ParentSQL( subSQL, parentSQL,count-i,subSQLLen);
            i++;
        }
        // 4 去掉两端()
        parentSQL=parentSQL.substring(1,parentSQL.length()-1);
        return parentSQL;
    }

    /**
     * 处理字符串连接:||-->concat()函数
     *
     * @param sql
     * @return
     */
    private static String convertStringRelation(String sql) {

        StringBuffer sWhere = new StringBuffer();
        sql = sql.trim().toUpperCase();

        //1、判断传过来的sql 是否包含 || 连接符
        if(sql.indexOf("||")>0){

            //1、处理 SELECT.....FROM 之间的 || 字符串连接符。
            int selectIndex = sql.indexOf("SELECT");
            int fromIndex = sql.indexOf("FROM");

            if (sql.indexOf("||") < fromIndex&&sql.indexOf("||") >0) {// 判断|| 连接符是否在 SELECT.....FROM之间
                String sfSql = sql.substring(selectIndex + 6, fromIndex);
                String[] sfSqls = sfSql.split(",");

                StringBuffer sfSqlNew = new StringBuffer();
                for (int i = 0; i < sfSqls.length; i++) {
                    String sfValue = sfSqls[i];
                    if (sfValue.indexOf("||") > 0) {//如果存在 || 连接符
                        //进行 ||-->concat()函数的替换,并将替换后的函数拼接到 StringBuffer sfSqlNew中。
                        String s = convertConcatBetweenSelectANDFrom(sfValue);
                        sfSqlNew.append(s + ",");
                    } else {//不存在|| 连接符,也加入到 StringBuffer sfSqlNew 中。
                        sfSqlNew.append(sfValue + ",");
                    }
                }

                //去除最后一个逗号,并替换SELECT......FROM 之间的列。
                sfSqlNew.deleteCharAt(sfSqlNew.toString().lastIndexOf(","));
                sql = "SELECT " + sfSqlNew + sql.substring(fromIndex);
            } else {

                //2、处理 WHERE 条件中的 || 字符串连接符。
                int index=sql.indexOf("||");
               while (index>0){
                   sql=convertSql_concat2(sql);
                   index=sql.indexOf("||");
               }

            }

        }

        return sql;
    }


    /**
     * 处理 WHERE 条件中的 || 连接字符串:|| -->concat() 函数
     *
     * @param values
     * @return
     */
    private static String convertConcatAfterWhere(String values) {
        String[] params = values.split("\\|\\|");
        StringBuffer newConition = new StringBuffer();
        newConition.append("CONCAT(");
        for (int i = 0; i < params.length; i++) {
            newConition.append(params[i] + ",");
        }
        newConition.deleteCharAt(newConition.toString().lastIndexOf(","));
        newConition.append(")");
        return newConition.toString();
    }



    /**
     * 处理 select......from 之间的 || 连接字符串
     *
     * @param sfValue
     * @return
     */
    private static String convertConcatBetweenSelectANDFrom(String sfValue) {

        if(sfValue.indexOf("AS")>0||sfValue.indexOf("as")>0){
            sfValue=sfValue.replace("AS", "");
            sfValue=sfValue.replace("as", "");
        }
        int index = sfValue.indexOf("||");
        //如果有别名,存取别名。并去掉别名
        String alias = "";
        int spaceIndex = sfValue.lastIndexOf(" ");
        if (spaceIndex > 0 && spaceIndex > index) {//如果成立则有别名,使用AS 关键字也可用该条件进行判断。
            alias = sfValue.substring(spaceIndex).trim();
            sfValue = sfValue.substring(0, spaceIndex);
        }


        String[] params = sfValue.split("\\|\\|");
        StringBuffer newConition = new StringBuffer();
        newConition.append("CONCAT(");
        for (int i = 0; i < params.length; i++) {
            newConition.append(params[i] + ",");
        }
        newConition.deleteCharAt(newConition.toString().lastIndexOf(","));
        newConition.append(") ");

        newConition.append(alias);


        return newConition.toString();
    }

    /**
     * SQL语句左右连接转换方法:
     * 思路:
     * 1、分解一个复杂的SQL为多个"不含任何子句的标准SQL子句";
     * 2、用原来的方法转换标准SQL子句;
     *
     * @param SQL
     * @return
     */
    private static String convertLRRelation(String SQL) {

        //1、*=左外连接,=*右外连接;sql7.0 时的用法。
        if (SQL.indexOf("(+)") < 0 && SQL.indexOf("(+)") < 0) {
            return SQL;
        }

        //2、如果是 INSERT 语句,忽略对左右连接的转换。
        if (SQL.startsWith("INSERT") && SQL.indexOf("VALUES") > 0) {
            return SQL;
        }

        //3-0、准备工作
        SQL = "(" + SQL + ")";
        Stack sqlStack = new Stack();// SQL子句堆栈
        Stack selectStack = new Stack();// SQL子句位置堆栈
        Stack braceStack = new Stack();// 左括句位置堆栈
        int count = 0;
        int temp;
        //3-1、 将字符串转换为字符数组
        char[] array = SQL.toCharArray();
        //3-2、 分析
        for (int i = 0; i < array.length; i++) {
            // 判断当前位置是否SQL子句开始,是则入栈
            if (isSQLStart(array, i)) {
                selectStack.push(new Integer(i));
            }
            //遇到左括号,则左括号入栈
            if (array[i] == '(') {
                braceStack.push(new Integer(i));
            }
            //遇到右括号,则左括号出栈
            if (array[i] == ')') {
                temp = ((Integer) braceStack.pop()).intValue();
                //如果出栈位置==SQL子句开始位置,则认为该SQL子句结束
                if (temp == ((Integer) selectStack.peek()).intValue()) {
                    //select子句出栈
                    selectStack.pop();
                    //处理SQL子句,并将SQL子句入栈
                    SQL = convert(sqlStack, SQL, temp, i, count);
                    count++;
                }
            }
        }

        // 4、 处理SQL子句的左/右连接,并组装SQL
        int i = 1;
        int subSQLLen;
        String subSQL;
        String parentSQL = SQL;
        while (!sqlStack.empty()) {
            subSQL = (String) sqlStack.pop();
            subSQLLen = subSQL.length();
            // 3.1 调用原来的左右连接转换方法
            subSQL = processLeftRight(subSQL);
            // 3.2 组装SQL
            parentSQL = insertSubSQL2ParentSQL(subSQL, parentSQL, count - i, subSQLLen);
            i++;
        }
        // 4 去掉两端()
        parentSQL = parentSQL.substring(1, parentSQL.length() - 1);
        return parentSQL;
    }

    /**
     * 组装 sql
     *
     * @param subSQL
     * @param parentSQL
     * @param num
     * @param subSQLLen
     * @return
     */
    private static String insertSubSQL2ParentSQL(String subSQL, String parentSQL, int num, int subSQLLen) {
        //定位子句在父句中的开始位置
        int index = parentSQL.indexOf(_SQL_PLACEHOLDER + num);
        //将占位符替换为子句
        parentSQL = parentSQL.substring(0, index)
                + subSQL
                + parentSQL.substring(index + subSQLLen);
        return parentSQL;
    }

    /**
     * 改进版的转换,支持多表间外连接。
     * 原则:
     * FROM A,B,C WHERE A.F_1 = B.F_1(+) AND B.F_2 = C.F_2(+)
     * AND B.F_3(+) = '11' AND C.F_3(+) = '22'
     * 转化为:
     * FROM A LEFT JOIN B ON A.F_1 = B.F_1
     * LEFT JOIN C ON B.F_2 = C.F_2
     * WHERE B.F_3 = '11' AND C.F_3 = '22'
     *
     * @param sql
     * @return
     */
    private static String processLeftRight(String sql) {

        //1、如果SQL中没有WHERE子句,则不转换,返回原sql;因为没有WHERE子句肯定没有左右连接。
        if (sql.toUpperCase().indexOf("WHERE") <= 0) {
            return sql;
        }

        //2、如果没有左右链接符号,则不转换;返回原sql。
        if (sql.indexOf("(+)") < 0 && sql.indexOf("(+)") < 0) {
            return sql;
        }


        //3、逐条读取 WHERE 条件
        int index;// 存储WHERE 关键字的开始索引。
        int indexend;// 存储 WHERE 条件结束位置的索引。
        int indexorder;// 存储 ORDER 关键字位置的索引

        index = sql.toUpperCase().indexOf(" WHERE ");
        indexend = sql.toUpperCase().indexOf(" GROUP ");
        indexorder = sql.toUpperCase().indexOf(" ORDER ");

        String sGroup="";
        String sOrderBy="";

        //筛选 WHERE 关键字后的 and 条件索引结束位置。(排除 GROUP、ORDER)
        if (indexend < 0)
            indexend = sql.length();//  排除 GROUP
        else
            sGroup=sql.substring(indexend,sql.length());//记录 Group By

        if (indexorder < indexend && indexorder > 0){
            indexend = indexorder;// 排除 ORDER
            sOrderBy=sql.substring(indexorder,sql.length());
        }

        int count = 1;
        StringBuffer sWhere = new StringBuffer();// 存储外连接转换后的语句
        StringBuffer andWhere = new StringBuffer();// 存储非表连接条件的 and 条件

        //3-1、 获取 FROM 关键字后的表名,应对如果有表的别名的情况。
        String vstables_alias = sql.substring(sql.toUpperCase().indexOf("FROM") + 4, index);
        String[] tables_alias = vstables_alias.split(",");

        //存储不需要连接的表,处理转换后表名丢失问题。
        Map<String,String> fromtable = new HashMap<String,String>();

        //3-2、处理WHERE .....; AND ....条件,进行左右连接的转换
        while (index > 0 && index < indexend) {
            int indexand;
            int space;
            String swhereand = "";

            // 逐条读取 and 条件
            indexand = sql.toUpperCase().indexOf(" AND ", index + 5);//从指定的索引处开始,返回第一次出现的指定子字符串在此字符串中的索引.
            if (indexand < 0)
                indexand = indexend;

            swhereand = sql.substring(index, indexand).trim();
            space = getOperPosM(swhereand, 0);// 分析逻辑运算符

            String tablel = "", tabler = "";// 左表名、右表名
            int indexl, indexr;// 存放别名引用时 . 的索引位置

            // 如果 space >0 说明需要翻译该条件
            if (space > 0) {

                // 获取 swhereand 条件表达式左部表名。暂不考虑表的别名
                indexl = swhereand.indexOf(".");
                if (indexl < space && indexl > 0) {
                    int spacel;
                    spacel = getOperPosL(swhereand, 0);
                    if (spacel < 0 || spacel > space) spacel = 0; //逻辑符号左面部分的表名
                    tablel = swhereand.substring(spacel + 1, indexl).trim();
                    tablel = dealExistTables_Alias(tables_alias, tablel );//处理表名是别名的情况。
                }

                // 获取 swhereand 条件表达式右部表名。
                indexr = swhereand.lastIndexOf(".");
                if (indexr > space && indexr > 0) {
                    int spacer;
                    spacer = getOperPosL(swhereand, space);
                    if (spacer < space) spacer = space;  //逻辑符号右面部分的表名
                    tabler = swhereand.substring(spacer + 1, indexr).trim();
                    tabler = dealExistTables_Alias(tables_alias, tabler );//处理表名是别名的情况。
                }

            }

            // 如果没有获取到表名前缀 并且条件中不包含(+) 字符,则不对该条件进行进行左右连接的转换
            if (tablel.length() > 0 || tabler.length() > 0) {//是否有表名前缀

                //常量表达式的左右连接处理
                if (tabler.length() == 0) {
                    //判断是否是右连接
                    if (swhereand.indexOf("(+)") > 0) {//是:待处理、暂不考虑

                    } else {//否
                        andWhere.append(swhereand + " ");
                    }
                } else {

                    if (swhereand.indexOf("(+)") > 0) {// WHERE 条件中包含 (+) 则进行翻译
                        if (swhereand.indexOf("=") > swhereand.indexOf("(+)")) {// 判断是左连接还是右连接:ture 则为右连接

                            swhereand = swhereand.replace("(+)", "");
                            swhereand = swhereand.replace("AND", "");
                            swhereand = swhereand.replace("WHERE", "");
                            if(sWhere.indexOf(tablel)>=0){//处理 表名重复问题
                                int onIndex =sWhere.indexOf("ON",sWhere.indexOf(tablel))+2;
                                sWhere.insert(onIndex,swhereand+" AND ");
                            }else{
                                sWhere.append(" RIGHT JOIN " + tablel + " ON " + swhereand + " ");
                            }

                        } else {//左连接

                            swhereand = swhereand.replace("(+)", "");
                            swhereand = swhereand.replace("AND", "");
                            swhereand = swhereand.replace("WHERE", "");
                            if(sWhere.indexOf(tabler)>=0){//处理 表名重复问题
                                int onIndex =sWhere.indexOf("ON",sWhere.indexOf(tabler))+2;
                                sWhere.insert(onIndex,swhereand+" AND ");
                            }else{
                                sWhere.append(" LEFT JOIN " + tabler + " ON " + swhereand + " ");
                            }
                        }
                    }else{//where 条件不包含外连接(+) 符号
                        if(swhereand.indexOf("OR")<0){//and 条件不含 OR 逻辑运算符时
                            if(!fromtable.containsKey(tabler)){
                                fromtable.put(tabler, tabler);
                            }
                            if(!fromtable.containsKey(tablel)){
                                fromtable.put(tablel, tablel);
                            }
                        }
                        andWhere.append(swhereand + " ");
                    }
                }
            } else {
                andWhere.append(swhereand + " ");
            }

            index = indexand;// 重新计算 index 控制循环,遍历下一个 AND 条件。

        }

        // 内连接需要存的表
        String vs_fromtables = "";
        Iterator<Map.Entry<String, String>> entries = fromtable.entrySet().iterator();
        while (entries.hasNext()) {
            Map.Entry<String, String> entry = entries.next();
            vs_fromtables +=entry.getKey()+",";
        }

        //设置外连接时默认主表
        if(vs_fromtables==null||vs_fromtables.equals("")){
            vs_fromtables+=tables_alias[0]+",";
        }
        vs_fromtables=vs_fromtables.substring(0,vs_fromtables.length()-1);

        String and_Where=andWhere.toString().toUpperCase();
        and_Where=and_Where.indexOf("WHERE")>=0?and_Where:and_Where.replaceFirst("AND", "WHERE");

        index = sql.toUpperCase().indexOf("FROM");
        sql = sql.substring(0, index) + "  FROM  ("+vs_fromtables +")" + sWhere + and_Where+sGroup+sOrderBy;

        return sql;
    }

    /**
     * 恢复表名+别名
     * @param tables_alias
     * @param tablel
     * @return
     */
//    private static String rollbackdealExistTables_Alias(String[] tables_alias, String tablel) {
//        for(int i=0;i<tables_alias.length;i++){
//            if(tables_alias[i].indexOf(tablel)>=0){
//                tablel=tables_alias[i];
//            }
//        }
//        return tablel;
//    }

    /**
     * 处理含有表别名的情况:如果and 条件中的表名是 FROM 后数据表的别名,则将表名和别名赋值给 tablel。
     *
     * @param tables_alias
     * @param table
     * @return
     */
    private static String dealExistTables_Alias(String[] tables_alias, String table ) {
        for (int i = 0; i < tables_alias.length; i++) {
            String table_alia = tables_alias[i].trim();
            int indexSpace = getOperPosL(table_alia, 0);

            if (indexSpace > 0) {//如果含有空格,则有别名
                String alias = table_alia.substring(indexSpace);//截取别名部分的字符串,有可能包含 AS 关键字
                int aliasIndex = alias.indexOf(table, 0);
                if (aliasIndex > 0&&alias.trim().length()==table.length()) {
                    table = table_alia;
                }
            }
        }
        return table;
    }

    /**
     * 主要为了获取条件中的表名
     * 分析 WHERE 条件中是否包含 "(", "=", ">", "<"," " 五种符号
     * 如果包含则返回该 符号的索引位置。
     *
     * @param strSql
     * @param begin
     * @return
     */
    private static int getOperPosL(String strSql, int begin) {
        String operList[] = new String[]{
                "(", "=", ">", "<", " "};
        int pos = -1;

        String strValue = "";
        int length = strSql.length();
        for (int i = begin; i < strSql.length(); i++) {
            strValue = strSql.substring(i, i + 1);
            for (int j = 0; j < operList.length; j++) {
                if (strValue.equals(operList[j])) {
                    return i;
                }
            }
        }
        return pos;
    }

    /**
     * 分析 WHERE 条件中是否包含 "=", ">", "<" 三种逻辑运算符
     * 如果包含则返回该 运算符的索引位置。
     *
     * @param strSql
     * @param begin
     * @return
     */
    private static int getOperPosM(String strSql, int begin) {
        String operList[] = new String[]{
                "=", ">", "<"};
        int pos = -1;

        String strValue = "";
        int length = strSql.length();
        for (int i = begin; i < strSql.length(); i++) {
            strValue = strSql.substring(i, i + 1);
            for (int j = 0; j < operList.length; j++) {
                if (strValue.equals(operList[j])) {
                    return i;
                }
            }
        }
        return pos;
    }


    /**
     * 抽取并转换SQL子句
     *
     * @param sqlStack
     * @param SQL
     * @param start
     * @param end
     * @param count
     * @return
     */
    private static String convert(Stack sqlStack, String SQL, int start, int end, int count) {
        // 1、 从SQL中抽取SQL子句,保留SQL子句外面的壳"即...(select ... from ...)...-->...(--SQLi--)..."
        String subSQL = SQL.substring(start + 1, end);
        sqlStack.push(subSQL);
        // 2、 并在原SQL子句位置回填子句标记
        int len = subSQL.length();
        int spaceCount = len - (_SQL_PLACEHOLDER.length() + 1);//(end-start-1)-(_SQL_PLACEHOLDER.length()+1);
        SQL = SQL.substring(0, start + 1) + _SQL_PLACEHOLDER + count + space("_", spaceCount) + SQL.substring(end);
        return SQL;
    }

    private static String space(String seed, int n) {
        StringBuffer buffer = new StringBuffer();
        for (int i = 0; i < n; i++) {
            buffer.append(seed);
        }
        return buffer.toString();
    }

    /**
     * 判断标准: 当前位置字符是"(" 且 后面的 Token为 "SELECT/UPDATE/INSERT等关键字"。
     *
     * @param array
     * @param postion
     * @return
     */
    private static boolean isSQLStart(char[] array, int postion) {
        boolean tag = false;
        if (array[postion] == '(') {
            // 取下一个Token
            String token = getNextToken(array, postion + 1);
            // 判断Token 是否为SQL子句开始关键字
            for (int i = 0; i < beginKeyList.length; i++) {
                if (token.toUpperCase().equals(beginKeyList[i])) {
                    tag = true;
                    break;
                }
            }
        }
        return tag;
    }

    /**
     * 获取当前位置开始的第一个 Token
     *
     * @param array
     * @param postion
     * @return
     */
    private static String getNextToken(char[] array, int postion) {
        StringBuffer token = new StringBuffer();
        //吃掉Token开始空格
        postion = getNextTokenStartPosition(array, postion);
        for (int i = postion; i < array.length; i++) {
            //循环判断该字符是否Token分隔符
            for (int k = 0; k < spaceCharList.length; k++) {
                if (array[i] == spaceCharList[k]) {
                    return token.toString().trim();
                }
            }
            //该字符不是分隔符,则认为是Token的字符
            token.append(array[i]);
        }
        return token.toString().trim();
    }

    /**
     * 吃空格--找到第一个非空字符位置。
     *
     * @param array
     * @param begin
     * @return
     */
    private static int getNextTokenStartPosition(char[] array, int begin) {
        char ch;
        //1 忽略begin后面的空格-找到"标志符"的开始位置
        for (int i = begin; i < array.length; i++) {
            int j = 0;
            for (; j < spaceList.length; j++) {
                if (array[i] == spaceList[j]) {
                    begin++;
                    break;
                }
            }
            //遇到第一个非分隔符(表明Token开始出现),则退出循环
            if (j == spaceList.length) {
                break;
            }
        }
        return begin;
    }


    /**
     * 专门处理 Update 语句
     *
     * @param sql
     * @return
     */
    private static String processUpdateOracleToMySQL(String sql) {

        int index, whereIndex;

        //1、对 sql 进行大写转换。
        String upperSql = sql.trim().toUpperCase();

        //2、如果整个语句没有 FROM 子句,则语句不进行转换;如果整个语句没有 WHERE 子句,则语句不进行转换。
        index = upperSql.lastIndexOf(" FROM ");
        if (index < 0) {
            return sql;
        }
        whereIndex = upperSql.lastIndexOf(" WHERE ");
        if (whereIndex < 0) {
            return sql;
        }

        //3、如果在FROM和WHERE之间有单引号或双引号,则不进行转换。
        String temp = upperSql.substring(index + 4, whereIndex);
        int nValue = temp.indexOf("'");
        if (nValue >= 0) {
            return sql;
        }
        nValue = temp.indexOf("\"");
        if (nValue >= 0) {
            return sql;
        }

        //4、如果有From子句,但也有Exists,说明此UPDATE可能是标准SQL,同样不做处理。
        if (upperSql.indexOf("EXISTS") > 0) {
            return sql;
        }

        //5、如果 set field = (select ...) or = isnull((select...则不做处理。
        String setclause = upperSql.substring(upperSql.indexOf(" SET ") + 5);
        setclause = setclause.substring(setclause.indexOf("=") + 1); //取 = 后面的部分
        setclause = setclause.replaceAll(" ", ""); //去掉所有空格 ;
        if (setclause.startsWith("(SELECT") ||
                setclause.startsWith("ISNULL((SELECT") ||
                setclause.startsWith("ISNULL(SELECT")) {
            return sql;
        }

        //6、默认为只要存在FROM子句,就应该存在WHERE
        String tabn = sql.substring(upperSql.indexOf("UPDATE") + 6, upperSql.indexOf(" SET ")).trim();
        String set = sql.substring(upperSql.indexOf(" SET ") + 5, index).trim();
        String from = sql.substring(upperSql.indexOf(" FROM ") + 6, upperSql.indexOf(" WHERE ")).trim();
        String astable = "";
        boolean ib = checkFromTable(from, tabn);
        if (ib && from.indexOf(",") > 0) {
            //截取本表之后的字符
            String vsTable = from.substring(from.indexOf(tabn) + tabn.length()).trim();
            if (vsTable.indexOf(",") >= 0) {//如果本表后边有逗号
                //看是否有别名
                astable = vsTable.substring(0, vsTable.indexOf(","));
                if (!astable.trim().equals("")) {//有别名
                    astable = astable.trim();
                }
                vsTable = vsTable.substring(vsTable.indexOf(",") + 1);
            } else {//没有逗号的情况
                astable = vsTable.trim();
                vsTable = "";
            }
            from = from.substring(0, from.indexOf(tabn)) + vsTable;
        }

        String seleCol = "", setCol = "";
        String where = sql.substring(upperSql.indexOf(" WHERE ") + 7).trim();
        setCol += set.substring(0, index);
        index = set.indexOf("=");
        set = set.substring(index + 1);

        while (set.trim().length() > 0) {
            index = set.indexOf("=");
            if (index >= 0) {
                temp = set.substring(0, index);
                set = set.substring(index + 1);
                for (int i = temp.length(); i > 0; i--) {
                    if (temp.substring(i - 1, i).equals(",")) {
                        setCol += "," + temp.substring(i).trim();
                        seleCol += "," + temp.substring(0, i - 1).trim();
                        break;
                    }
                }
            } else {
                seleCol += "," + set;
                set = "";
            }
        }

        if (setCol.startsWith(",")) {
            setCol = setCol.substring(1);
        }
        //加( )
        if (!setCol.trim().startsWith("(")) {
            setCol = "(" + setCol + ")";
        }
        if (seleCol.startsWith(",")) {
            seleCol = seleCol.substring(1);
        }

        temp = from;
        from = "";
        String alias = "";
        String temp1;
        while (temp.trim().length() > 0) {
            index = temp.indexOf(",");
            if (index >= 0) {
                temp1 = temp.substring(0, index);
                temp = temp.substring(index + 1);
            } else {
                temp1 = temp;
                temp = "";
            }
            if (temp1.trim().length() > tabn.length() &&
                    tabn.equals(temp1.trim().substring(0, tabn.length())) &&
                    temp1.substring(tabn.length(), tabn.length() + 1).equals(" ")) {
                alias = temp1.substring(tabn.length()).trim();
            } else {
                from += "," + temp1;
            }
        }
        if (from.trim().length() > 0 && from.startsWith(",")) {
            from = from.substring(1);
        }


        //where 中的 where要去掉 group 字句
        String where1 = where;
        if (upperSql.indexOf(" GROUP ") > 0) {
            where1 = sql.substring(upperSql.indexOf(" WHERE ") + 7, upperSql.indexOf(" GROUP ")).trim();
        }
        sql = "update " + tabn + " " + alias + " set " + setCol +
                "  = ( select " + seleCol + " from " + from + " where " +
                where + " ) where exists ( select 1 from " + from +
                " where " + where1 + " ) ";
        //替换别名
        if (!astable.equals("")) {
            sql = StringFunction.replaceString(sql, astable + ".", tabn + ".");
        }
        return sql;
    }

    /**
     * 检查update SQL 中 from 子句中,是否包含sTable表
     *
     * @param sFrom
     * @param sTable
     * @return
     */
    private static boolean checkFromTable(String sFrom, String sTable) {
        int ib = sFrom.indexOf(sTable);
        if (ib >= 0) {
            String sRem = sFrom.substring(ib + sTable.length(), sFrom.length());
            if (sRem != null && !sRem.trim().equals("")) {
                if (sRem.startsWith(" ") || sRem.startsWith(",")) {
                    return true;
                }
            } else {
                return true; //后面没内容 也返回真
            }
        }
        return false;
    }

    /**
     * 判断 SQL 是否需要转换:根据 SQL Head 标志,判断该SQL语句是否需要做跨平台语法转换
     *
     * @param sql
     * @return
     */
    private static boolean isNeedConvert(String sql) {

        boolean tag = true;
        //1、将所有不需要转换的HeadTag装入数组
        String[] head = new String[]{
                ConvertSql._SQL_HEAD_NO_CONVERT,
                ConvertSql._SQL_HEAD_CONVERTED_ORACLE_TO_MYSQL
        };
        //2、判断 sql 是否含有 数组中的 tag 头标记。若有:则不需要转换
        for (int i = 0; i < head.length; i++) {
            if (sql.startsWith(head[i])) {
                tag = false;
                break;
            }
        }
        return tag;
    }

    /**
     * 处理 WHERE 条件中的 || 连接符
     * @param pSql
     * @return
     */
    public static String convertSql_concat2(String pSql) {
        String convertSql = null;
        pSql=pSql+" ";// 如果 || 连接符条件在sql 的最后,则需要手工补个空格。
        int idx_concat = pSql.indexOf("||");
        if (idx_concat >= 0) {
            int idx_space1 = -1, idx_space2 = -1, idx_data = -1;
            for (int i = idx_concat - 1; i >= 0; i--) {
                char cc = pSql.charAt(i);
                if (Character.isSpace(cc)) {
                    if (idx_data != -1) {
                        idx_space1 = i;
                        break;
                    }
                } else {
                    idx_data = i;
                }
            }
            idx_data = -1;
            for (int i = idx_concat + 2; i <= pSql.length() - 1; i++) {
                char cc = pSql.charAt(i);
                if (Character.isSpace(cc)) {
                    if (idx_data != -1) {
                        idx_space2 = i;
                        break;
                    }
                } else {
                    idx_data = i;
                }
            }
            String rightStr = pSql.substring(idx_space1 + 1, idx_space2);
            rightStr=rightStr.replace("(","");//如果 条件中含有 左右括号则去除
            rightStr=rightStr.replace(")","");

            String[] strss = rightStr.split("\\|\\|");
            String ss = "CONCAT(" + rightStr.replaceFirst("\\|\\|", ",") + ")";
            convertSql = pSql.replaceFirst(strss[0] + "\\|\\|" + strss[1], ss);
        } else {
            convertSql = pSql;
        }
        return convertSql;

    }
}

StringFunction.java

package com.utils;

import java.util.*;

public class StringFunction {

    /**
     * 字符串替换函数
     *
     * @param psStr 要进行处理的参数字符串
     * @param psS   被替换的字符串
     * @param psD   替换的字符串
     * @return psStr  被替换后的字符串。
     */
    public static String replaceString(String psStr, String psS, String psD) {
        int viPos = psStr.indexOf(psS);
        if (viPos < 0)
            return psStr;
        int viLength = psS.length();
        StringBuffer vsValue = new StringBuffer();
        while (viPos >= 0) {
            vsValue.append(psStr.substring(0, viPos));
            vsValue.append(psD);
            psStr = psStr.substring(viPos + viLength);
            viPos = psStr.indexOf(psS);
        }
        vsValue.append(psStr);
        return vsValue.toString();
    }

}

posted @ 2021-08-21 21:57  星命定轨  阅读(1821)  评论(0编辑  收藏  举报