Oracle 超长SQL分割

Oracle使用SQL方式导入导出,plsql不支持导出含有clob等字段类型的表,使用DbVisualizer可以导出。
但字段长度过长,SQL会执行失败,需要使用存储过程将字段用变量替换,批量替换代码在下方,执行方法SQLSplitUtil.split(sql_path);


import java.io.*;
import java.util.*;

/**
 * Oracle SQL分割
 * 1. 当字段值长度超过3000时,使用存储过程
 * 2. 当字段值长度超过30000时,使用变量拼接
 * 3. 当字段值中有换行符时,使用存储过程
 *
 * 缺陷:
 * 1.当SQL中有多个字段总长度大于临界值,而单字段没有,无法分割
 * 2.当SQL有多个字段长度大于临界值,每次只能处理一个字段
 */
public class SQLSplitUtil {

    private static final String keyWords = "INSERT";
    private static final char matchSymbols = '\'';
    private static String resultPath = "";
    private static int bracketsCount = 0;
    private static int quotesCount = 0;
    private static int quoteIndex = -1;

    private static File getFileByPath(String path) throws FileNotFoundException {
        File file = new File(path);
        if (file.exists() && file.isFile()) {
            return file;
        } else {
            throw new FileNotFoundException("SQL FILE NOT FOUND.");
        }
    }

    private static String analysis(List<String> sqls, String table) {
        String result = "";
        String prefix = "prompt Loading " + table + "...\n";
        String suffix = "prompt " + sqls.size() + " records loaded\n";
        StringBuilder processor = new StringBuilder("declare \n");
        boolean hasValue = false;
        int count = 1;
        List<String> sqls2 = new ArrayList<>();
        for (String sql : sqls) {
//            System.out.println(sql);
            int length = byteLengthOfLine(sql);
            String param = "v_clob_" + table + "_" + count;
            //当行内含换行符时
            if (sql.contains("\n")) {
                count++;
                Map<String, Object> map = stringMultipleLines(sql, param);
                sqls2.add((String) map.get("sql"));
                processor.append(map.get("processor"));
                hasValue = (boolean) map.get("flag");
            } else if (length >= 3000) {
                count++;
                Map<String, Object> map = string3000(sql, param);
                sqls2.add((String) map.get("sql"));
                processor.append(map.get("processor"));
                hasValue = (boolean) map.get("flag");
            } else {
                sqls2.add(sql);
            }
        }
        StringBuilder statement = new StringBuilder();
        for (String s : sqls2) {
            statement.append(s).append("\n");
        }
        if (hasValue) {
            result += processor + "begin\n" + statement + "end;\n/\n";
        } else {
            result = statement.toString();
        }
        return prefix + result + suffix + "commit;";

    }

    private static Map<String, Object> string3000(String sql, String param) {
        Map<String, Object> result = new HashMap<>();
        result.put("flag", false);
        StringBuilder processor = new StringBuilder();
        StringBuilder sb = new StringBuilder();
        StringBuffer value = new StringBuffer();
        int tmpLength = 0;
        boolean isCount = false;
        for (int i = 0; i < sql.length(); i++) {
            char c = sql.charAt(i);
            if (c == matchSymbols || isCount) {
                value.append(c);
            } else {
                sb.append(c);
            }
            if (c == matchSymbols && (i == 0 || sql.charAt(i - 1) != '\\')) {
                isCount = !isCount;
            }
            //结束计数状态,判断字段值中长度
            if (!isCount) {
                //如果长度大于28000,使用存储过程,需要变量拼接
                if (tmpLength >= 28000) {
                    Map<String, Object> map = handleTooLongString(value.toString(), param);
                    processor.append(map.get("processor"));
                    sb.append(map.get("params"));
                    result.put("flag", true);
                } else if (tmpLength >= 2000) {//如果长度大于2000,使用存储过程
                    processor.append(param).append(" clob:=").append(handleLongString(value.toString())).append(";\n");
                    sb.append(param);
                    result.put("flag", true);
                } else {
                    sb.append(value);
                }
                value = new StringBuffer();
                tmpLength = 0;
            }else{
                tmpLength += bytesOfChar(c);
            }
        }
        result.put("processor", processor.toString());
        result.put("sql", sb.toString());
        return result;
    }

    private static Map<String, Object> stringMultipleLines(String sql, String param) {
        Map<String, Object> result = new HashMap<>();
        result.put("flag", false);
        StringBuilder processor = new StringBuilder();
        boolean isCount = false;
        StringBuilder sb = new StringBuilder();
        StringBuffer value = new StringBuffer();
        for (int i = 0; i < sql.length(); i++) {
            char c = sql.charAt(i);
            //如果是引号或者正在计数中,追加至字段值
            if (c == matchSymbols || isCount) {
                value.append(c);
            } else {//否则追加进SQL
                sb.append(c);
            }
            //如果是引号,并且不是\',计数状态改变
            if (c == matchSymbols && (i == 0 || sql.charAt(i - 1) != '\\')) {
                isCount = !isCount;
            }
            //结束计数状态,判断字段值中是否有换行符
            if (!isCount) {
                if (value.toString().contains("\n")) {
                    processor.append(param).append(" clob:=").append(value).append(";\n");
                    result.put("flag", true);
                    sb.append(param);
                } else {
                    sb.append(value);
                }
                value = new StringBuffer();
            }
        }
        result.put("processor", processor.toString());
        result.put("sql", sb.toString());
        return result;
    }

    private static String handleLongString(String value) {
        int tmpLength = 0;
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < value.length(); i++) {
            char c = value.charAt(i);
            tmpLength += bytesOfChar(c);
            //当长度超过2000时,进行换行
            if (tmpLength >= 2000) {
                sb.append("\n");
                tmpLength = 0;
            }
            sb.append(c);
        }
        return sb.toString();
    }

    private static Map<String, Object> handleTooLongString(String sql, String param) {
        boolean isCount = false;
        StringBuilder sb = new StringBuilder();
        StringBuffer value = new StringBuffer();
        int tmpLength = 0;
        int count = 1;
        StringBuilder params = new StringBuilder();
        for (int i = 0; i < sql.length(); i++) {
            char c = sql.charAt(i);
            if (c == matchSymbols || isCount) {
                value.append(c);
            } else {
                sb.append(c);
            }
            if (c == matchSymbols && (i == 0 || sql.charAt(i - 1) != '\\')) {
                isCount = !isCount;
            }
            if (isCount) {
                tmpLength += bytesOfChar(c);
                //当长度超过20000时,进行拼接
                if (tmpLength >= 20000) {
                    value.append("'");
                    String tmp = param + "_p" + count;
                    params.append(tmp).append("||");
                    sb.append(tmp).append(" clob:=").append(handleLongString(value.toString())).append(";\n");
                    tmpLength = 0;
                    value = new StringBuffer("'");
                    count++;
                }
            }
        }
        String tmp = param + "_p" + count;
        params.append(tmp);
        sb.append(tmp).append(" clob:=").append(handleLongString(value.toString())).append(";\n");

        Map<String, Object> map = new HashMap<>();
        map.put("processor", sb.toString());
        map.put("params", params.toString());
        return map;
    }

    /**
     * 判断字符的字节数
     *
     * @param c char
     * @return length
     */
    private static int bytesOfChar(char c) {
        if (c <= 255) {
            return 1;
        } else {
            return 2;
        }
    }

    //获取字符串的字节数
    private static int byteLengthOfLine(String line) {
        int length = 0;
        for (int i = 0; i < line.length(); i++) {
            length += bytesOfChar(line.charAt(i));
        }
        return length;
    }

    private static void readFile(File file) throws IOException {
        BufferedReader br = new BufferedReader(new FileReader(file));
        List<String> sqls = new ArrayList<>();
        boolean finish = true;
        boolean finishLine = false;
        boolean start = false;
        String line;
        String tmpLine = "";
        String previousTable = "";

        while ((line = br.readLine()) != null) {
            //如果上一行未结束,合并本行统计
            if (!finish) {
                finish = statisticBrackets(line);
                if (!finish) {
                    tmpLine += line + "\n";
                    continue;
                }
                finishLine = true;
                tmpLine += line;
            }
            //如果以INSERT开头,或者上一行未结束,或者表名与上一行相同
            //否则直接写入文件
            if (start || line.trim().toUpperCase().startsWith(keyWords) || finishLine) {
                //如果当前行为结束行,则将多行合成的一行作为实际数据
                if (finishLine) {
                    line = tmpLine;
                } else {
                    finish = statisticBrackets(line);
                    if (!finish) {
                        tmpLine += line + "\n";
                        continue;
                    }
                }
                //如果当前行为空或者非INSERT开头,表示需要进行上一张表的插入
                if ("".equals(line.trim()) || !line.trim().toUpperCase().startsWith(keyWords)) {
                    start = false;
                    String result = analysis(sqls, previousTable);
                    //先写入SQL,再写入当前行,保证文本结构统一
                    writeFile(result);
                    writeFile(line + "\n");
                    sqls.clear();
                    previousTable = "";
                } else {
                    start = true;
                    //获取当前行表名
                    String lineTrim = line.trim().substring(keyWords.length()).trim().substring("INTO".length()).trim();
                    String table = lineTrim.substring(0, lineTrim.indexOf(" ")).replaceAll("`", "");
                    //如果当前表名和上一张插入的表不是同一张表,表示需要进行上一张表的插入
                    if (!previousTable.equals(table)) {
                        if (!"".equals(previousTable)) {
                            String result = analysis(sqls, previousTable);
                            writeFile(result);
                            sqls.clear();
                        }
                        previousTable = table;
                    }
                    sqls.add(line);
                }
                //设置为false,以便为下一个多行数据使用
                finishLine = false;
                tmpLine = "";
            } else {
                writeFile(line + "\n");
            }
        }
        writeFile(analysis(sqls, previousTable));
        sqls.clear();
    }

    /**
     * 边界检测 判断SQL括号是否闭合
     *
     * @param line line
     * @return line finish or not
     */
    private static boolean statisticBrackets(String line) {
        if ("".equals(line.trim())) {
            return true;
        }
        for (int i = 0; i < line.length(); i++) {
            char c = line.charAt(i);
            if (quoteIndex == -1 && (c == '(' || c == ')')) {
                bracketsCount += Math.pow(-1, '(' - c);
            }
            //排除字段值中含有括号的情况以及字段中的引号
            if (c == matchSymbols && (i == 0 || line.charAt(i - 1) != '\\')) {
                quotesCount = (quotesCount + 1) % 2;
                quoteIndex = quotesCount == 0 ? -1 : i;
            }
        }
        return quotesCount == 0 && bracketsCount == 0 && line.endsWith(";");
    }

    private static void writeFile(String content) throws IOException {
        OutputStream os = new FileOutputStream(new File(resultPath), true);
        os.write(content.getBytes());
        os.flush();
        os.close();
    }

    private static void deleteFileByPath(String path) {
        File file = new File(path);
        if (file.exists() && file.isFile()) {
            file.delete();
        }
    }

    public static void split(String path) {
        try {
            File file = getFileByPath(path);
            String resultName = file.getName().substring(0, file.getName().lastIndexOf(".")) + "_split.sql";
            resultPath = file.getParentFile() + File.separator + resultName;
            deleteFileByPath(resultPath);
            readFile(file);
            System.out.println(resultPath);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值