• 五、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_
    ";
        //由 Oracle 转换之后得到的 MySQL
        private final static String _SQL_HEAD_CONVERTED_ORACLE_TO_MYSQL = "--_CONVERTED_ORACLE_TO_MYSQL_
    ";
    
        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();
        }
    
    }

  • 相关阅读:
    windows测试模式
    架构设计之Spring-Session的分布式集群会话管理
    WPF集合
    java Socket Udp
    java Socket 获取本地主机ip
    快速排序
    java 正则举例
    JNI 在命令行窗口输入字符,不显所输入字符,显指定的掩饰符
    java 在控制台上输入密码时,密码不显示在控制台上
    java 获取对象大小
  • 原文地址:https://www.cnblogs.com/pengguozhen/p/15170699.html
Copyright © 2020-2023  润新知