代码编织梦想

因为是逐行读取逐行写入的,所以经过10G的sql文件测试,目前无内存溢出异常

首先我封装了一个校验sql字段类型的工具类,这个是用来检验sql建表语句中的字段的

public class CheckSqlType {

    /**
     * 判断字符串是否包含sql字段类型
     * @param sql
     * @return
     */
    public static Boolean isSqlType(String sql) {
        String sqlLowerCase = sql.toLowerCase();
        String type = "text,memo,byte,integer,long,single,double,currency,autonumber,date,time," +
                "yes,no,ole object,hyperlink,lookup wizard,char,varchar,tinytext,blob,mediumtext," +
                "mediumblob,longtext,longblob,enum,set,tinyint,smallint,mediumint,int,bigint,float," +
                "decimal,datetime,timestamp,year,nchar,nvarchar,ntext,bit,binary,varbinary,image,numeric," +
                "smallmoney,money,real,datetime2,smalldatetime,datetimeoffset,sql_variant,uniqueidentifier," +
                "xml,cursor,table";

        String[] types = type.split(",");
        for (String sqlType : types) {
            if (sqlLowerCase.contains(" "+sqlType)){
                return true;
            }
        }

        return false;
    }

}

然后就是解析sql语句的算法了,将解析好的数据字段值写入输入的磁盘路径下text/库名/表名/字段名.txt中(因为sql文件是以库名命名的)



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

/**
 * 
 * @description 解析sql语句的算法,获取sql语句中的表名,字段名及字段值并存到磁盘,可多线程同时运行
 *
 * @author: 影耀子(YingYew)
 * @create: 2023-03-18 16:28
 **/
public class AnalysisSql {

    /**
     * 解析sql语句的算法,获取sql语句中的表名,字段名及字段值,可多线程同时运行
     * @param sqlPath sql语句文件所在目录,读取此目录下所有后缀名为.sql的文件
     * @param textPath 要写入磁盘的路径
     * @throws Exception
     */
    public void parseSql(String sqlPath,String textPath) throws Exception {
        // KEY为表名,value为表字段(按顺序添加进集合中)
        Map<String, List<String>> tableInfo = new HashMap<>();

        // 获取所有sql文件名
        String[] list = new File(sqlPath).list();

        // 根据文件名创建对应文件夹
        for (String s : list) {
            //数据库文件地址
            String sqlFilePath = textPath+"/text/"+s.replace(".sql","");
            // 根据文件名(数据库名)创建对应文件夹
            if (s.endsWith(".sql")){
                new File(sqlFilePath).mkdirs();
            }

            // 读取sql文件,逐行读取
            BufferedReader sqlReader = new BufferedReader(new FileReader(sqlPath+s));
            String sqlContext = sqlReader.readLine();

            // 根据“;”区分的一行sql语句
            StringBuilder sqlOneLine = new StringBuilder(sqlContext);
            while (sqlContext != null){
                String nextLine = sqlReader.readLine();

                if (sqlContext.endsWith(";")) {
//                    System.out.println(sqlOneLine);
                    // sqlOneLine就是截取的一条sql语句,开始业务编写

                    // 判断sql语句并写入磁盘文件
                    sqlWriteIn(sqlOneLine.toString(),sqlFilePath,tableInfo);

                    // 清空sqlOneLine字符
                    sqlOneLine.setLength(0);
                }
                // 注释不加入
                String sqlOneLineStr = sqlOneLine.toString();
                if (sqlOneLineStr.endsWith("*/") && sqlOneLineStr.contains("/*")){
                    sqlOneLine.delete(sqlOneLineStr.indexOf("/*"),sqlOneLineStr.indexOf("*/")+2);
                }

                if (nextLine != null && !nextLine.startsWith("--")){
                    sqlOneLine.append(nextLine);
                }

                sqlContext = nextLine;
            }
            sqlReader.close();
            System.out.println("全部运行成功,sql名列表:"+list);
        }
    }

    /**
     * 处理一条sql的逻辑
     *  在path路径下创建表名的文件夹
     *
     * @param sql sql语句
     * @param path 、需要写入的路径
     * @param tableInfo 、KEY为表名,value为表字段(按顺序添加进集合中)
     */
    public void sqlWriteIn(String sql,String path,Map<String, List<String>> tableInfo) throws Exception {
        String sqlLowerCase = sql.toLowerCase();
        // 建库语句
        if (sqlLowerCase.contains("create ")
                && sqlLowerCase.contains(" table ")){

            String table = sql.substring(sqlLowerCase.indexOf(" table") + 6, sqlLowerCase.indexOf("("))
                    .trim().replace("'","").replace("`","")
                    .replace("\"","");

            List<String> sqlFieldList = new ArrayList<>();

            // 创建表名文件夹
            new File(path+"/"+table).mkdir();
            new File(path+"/"+table+"/field").mkdir();
            String[] sqlFieldInformations = sql.substring
                    (sqlLowerCase.indexOf("(")+1, sqlLowerCase.lastIndexOf(")"))
                    .split(",");

            for (String sqlFieldInformation : sqlFieldInformations) {
                sqlFieldInformation = sqlFieldInformation.trim();
                String sqlFieldName = sqlFieldInformation
                        .substring(0, sqlFieldInformation.indexOf(" "));
                sqlFieldName = sqlFieldName.replace("`","")
                        .replace("'","")
                        .replace("\"","");

                // 判断此行数据是否是字段
                if (!CheckSqlType.isSqlType(sqlFieldInformation)){
                    break;
                }
                // 根据字段名创建字段文件text
                new File(path+"/"+table+"/field/"+sqlFieldName+".txt").createNewFile();
                sqlFieldList.add(sqlFieldName);
            }
            tableInfo.put(table,sqlFieldList);

            return;
        }

        // 添加语句
        if (sqlLowerCase.contains("insert ")
                && sqlLowerCase.contains(" into ")){

            String table = sql.substring(sqlLowerCase.indexOf(" into ") + 6, sqlLowerCase.indexOf(" values"))
                    .trim().replace("'","").replace("`","")
                    .replace("\"","");

            String substring = sql.substring
                    (sqlLowerCase.indexOf(" values") + 7, sqlLowerCase.lastIndexOf(")"));
            String sqlFieldValuesStr = substring
                    .substring(substring.indexOf("(")+1);

            // 处理有问题的数据,
            List<String> sqlFieldValues = handle(sqlFieldValuesStr);

            List<String> tableFieldNameList = new ArrayList<>();
            // 如果添加语句包含字段名,则使用添加语句的字段名
            if (table.contains("(") && table.contains(")")){
                String[] tableFieldName = table.substring(table.indexOf("(") + 1, table.indexOf(")")).split(",");
                tableFieldNameList.addAll(Arrays.asList(tableFieldName));
                table = table.substring(0,table.indexOf("("));
            }
            int tableFieldNameListSize = tableFieldNameList.size();
            List<String> DefaultTableFieldNameListlist = tableInfo.get(table);

            if (sqlFieldValues.size() != tableFieldNameListSize &&
                    sqlFieldValues.size() != DefaultTableFieldNameListlist.size()){

                throw new Exception("表字段长度与数据长度不符---表名:"+table);
            }
            BufferedWriter bufferedWriter;
            int sqlFieldValuesLen = sqlFieldValues.size();
            for (int i = 0; i < sqlFieldValuesLen; i++) {
                String sqlFieldValue = sqlFieldValues.get(i);

                // 判断添加语句中有无字段
                if (tableFieldNameListSize > 0){
                    bufferedWriter = new BufferedWriter(new FileWriter
                            (path + "/" + table + "/field/" + tableFieldNameList.get(i) + ".txt",true));
                }else {

                    bufferedWriter = new BufferedWriter(new FileWriter
                            (path + "/" + table + "/field/" + DefaultTableFieldNameListlist.get(i) + ".txt",true));
                }
                bufferedWriter.append(sqlFieldValue);
                bufferedWriter.newLine();
                bufferedWriter.flush();// 清除缓存
                bufferedWriter.close();
            }

        }



    }

    /**
     * '15465', '全体成员(无人武部,交警莲湖大队)', null, null,
     * '1', '2266', '3071', '2021-05-18 15:36:32', '2021-04-27 14:43:47', null, null
     *
     * @author : YingYew影耀子
     * @date : 2023/3/16 15:06
     * @param :null
     * @return :null
     **/

    public List<String> handle(String sqlFieldValuesStr) {
        StringBuilder sqlFieldValuesStrBuilder = new StringBuilder(sqlFieldValuesStr);

//        System.out.println(sqlFieldValuesStr);
        List<String> sqlFieldValueStrList = new ArrayList<>();


        /** 1,从后往前读取,判断是否以引号结尾,
         *   截取以最后一个,到最后一行的数据
         *      是:去除前后空格及引号
         *      否:去除前后空格
         *
         * @author : 影耀子(YingYew)
         * @date : 2023/3/16 16:51
         * @param :args
         **/

        while (sqlFieldValuesStrBuilder.length() > 0){
            String sqlFieldValuesBuilderStr = sqlFieldValuesStrBuilder.toString();
            int length = sqlFieldValuesStrBuilder.length();

            String sqlFieldValue;
            if (sqlFieldValuesBuilderStr.endsWith("'")){
                sqlFieldValue = getSqlFieldValue("'",sqlFieldValuesStr,length,sqlFieldValuesStrBuilder);
            }else if (sqlFieldValuesBuilderStr.endsWith("\"")){
                sqlFieldValue = getSqlFieldValue("\"",sqlFieldValuesStr,length,sqlFieldValuesStrBuilder);
            }else if (sqlFieldValuesBuilderStr.endsWith("`")){
                sqlFieldValue = getSqlFieldValue("`",sqlFieldValuesStr,length,sqlFieldValuesStrBuilder);
            }else{
                int lastIndex = sqlFieldValuesStr.lastIndexOf(",", length - 1);
                // 因为无引号,所以截取到结尾
                sqlFieldValue = sqlFieldValuesStr.substring
                        (lastIndex+1, length).trim();
//                .replace(",","").trim()
                if (lastIndex != -1){
                    sqlFieldValuesStrBuilder.setLength(lastIndex);
                }else {
                    sqlFieldValuesStrBuilder.setLength(0);
                }
            }
            sqlFieldValueStrList.add(sqlFieldValue);
        }
        Collections.reverse(sqlFieldValueStrList);

        return sqlFieldValueStrList;
    }


    /**
     * 获取sql语句中的值
     * @author : YingYew影耀子
     * @date : 2023/3/17 10:20
     * @return :java.lang.String
     **/
    public String getSqlFieldValue(String quotation,String sqlFieldValuesStr,int length,StringBuilder sqlFieldValuesStrBuilder){

        // 获取字段值的开始下标
        int lastIndexQuote = sqlFieldValuesStr.lastIndexOf(quotation, length - 2);

        // 若检测到\,则特殊处理得到字段值开始下标,
        while (sqlFieldValuesStr.startsWith("\\", lastIndexQuote-1)){
            lastIndexQuote = sqlFieldValuesStr.lastIndexOf(quotation, lastIndexQuote-1);
        }
        // 获取字段值前面的“,”开始的下标
        int lastIndexComma = sqlFieldValuesStr.lastIndexOf(",", lastIndexQuote-1);
        // 获取字段值,不包含包含引号
        String sqlFieldValueQuestion = sqlFieldValuesStr.substring
                (lastIndexQuote+1, length-1);

        if (lastIndexComma != -1){
            sqlFieldValuesStrBuilder.setLength(lastIndexComma);
        }else {
            sqlFieldValuesStrBuilder.setLength(0);
        }

        // 正确的值
        return analyseSprit(sqlFieldValueQuestion);
    }


    /**
     * 解析斜杠
     * 未\\\\央\\\\\区\'各\\\街\\道    输出:未\\央\\区'各\街\道
     *  斯蒂芬\\r\\n斯蒂芬\\r\\n斯蒂芬\\r\\n斯蒂芬\\r\\n斯蒂芬\\r\\n斯蒂芬\\r\\n斯蒂芬
     * 输出:斯蒂芬\r\n斯蒂芬\r\n斯蒂芬\r\n斯蒂芬\r\n斯蒂芬\r\n斯蒂芬\r\n斯蒂芬
     *
     * @date : 2023/3/17 11:29
     * @param :sqlFieldValuesStr
     * @return :java.lang.String
     **/
    public String analyseSprit(String sqlFieldValue) {

        if (!sqlFieldValue.contains("\\")){
            return sqlFieldValue;
        }
//        sqlFieldValue = sqlFieldValue.replace("\\"+quotation,quotation);

        // 解析斜杠的逻辑
        StringBuilder sqlFieldValueStringBuilder = new StringBuilder(sqlFieldValue);

        List<String> handleSqlFieldValueFallList = new ArrayList<>();
        while (sqlFieldValueStringBuilder.length() > 0){
            // 获取最后处理\的下标
            int barEndIndex = sqlFieldValue.lastIndexOf("\\",sqlFieldValueStringBuilder.length()-1);
            if (barEndIndex == -1){
                handleSqlFieldValueFallList.add(sqlFieldValueStringBuilder.toString());
                break;
            }
            // 获取每个\开始的下标
            int barStartIndex = barEndIndex;
            while (sqlFieldValue.startsWith("\\",barStartIndex-1)){
                barStartIndex--;
            }
            // 获取杠的个数
            int barNum = barEndIndex-barStartIndex+1;
            // 正确的杠的个数
            int correctBarNum = barNum/2;
            StringBuilder bar = new StringBuilder();
            for (int i = 0; i < correctBarNum; i++) {
                bar.append("\\");
            }
            handleSqlFieldValueFallList.add(
                    bar.append(sqlFieldValue, barEndIndex+1, sqlFieldValueStringBuilder.length()).toString());
            sqlFieldValueStringBuilder.setLength(barStartIndex);
        }
        Collections.reverse(handleSqlFieldValueFallList);
        StringBuilder handleSqlFieldValue = new StringBuilder();
        for (String s :handleSqlFieldValueFallList){
            handleSqlFieldValue.append(s);
        }
        return handleSqlFieldValue.toString();
    }

}

此代码已测试过,可多线程运行过10G内容的sql文件,无内存溢出

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/yaoyipeng/article/details/129677830

sql语句执行过程详解-爱代码爱编程

第一步:编译阶段 1)查询高速缓存(library cache) 服务器进程在接到客户端传送过来的SQL语句时,不会直接去数据库查询。服务器进程把这个SQL语句的字符转化为ASCII等效数字码,接着这个ASCII码被传递给一个HASH函数,并返回一个hash值,然后服务器进程将到shared pool中的library cache(高速缓存)中去查找是

oracle sql语句执行流程与顺序原理解析_风神修罗使的博客-爱代码爱编程

第一步:客户端把语句发给服务器端执行 当我们在客户端执行SQL语句时,客户端会把这条SQL语句发送给服务器端,让服务器端的进程来处理这语句。也就是说,Oracle 客户端是不会做任何的操作,他的主要任务就是把客户端产生的一

oracle sql语句执行流程与顺序原理解析_jerry-89的博客-爱代码爱编程

Oracle语句执行流程 第一步:客户端把语句发给服务器端执行 当我们在客户端执行SQL语句时,客户端会把这条SQL语句发送给服务器端,让服务器端的进程来处理这语句。也就是说,Oracle 客户端是不会做任何的操作,他的主要任务就是把客户端产生的一些SQL语句发送给服务器端。服务器进程从用户进程把信息接收到后, 在PGA 中就要此进程分配所需内存,存储相关

sql语句执行过程详解(执行计划)_专注_每天进步一点点的博客-爱代码爱编程_sql代码怎么运行

SQL语句执行过程详解 一条sql,plsql的执行到底是怎样执行的呢? 一、SQL语句执行原理: 第一步:客户端把语句发给服务器端执行 当我们在客户端执行 select 语句时,客户端会把这条 SQL 语句发送给服务器端,让服务器端的 进程来处理这语句。也就是说,Oracle 客户端是不会做任何的操作,他的主要任务就是把客户端产生 的一些 SQL 语句发

SQL语句执行流程与顺序原理详解-爱代码爱编程

目录 Oracle语句执行流程   第一步:客户端把语句发给服务器端执行   第二步:语句解析   第三步:绑定变量赋值   第四步:语句执行   第五步:提取数据SQL语句执行顺序 Oracle语句执行流程 第一步:客户端把语句发给服务器端执行 当我们在客户端执行SQL语句时,客户端会把这条SQL语句发送给服务器端,让服务器端的进

面试必备:一条sql语句的执行过程-爱代码爱编程

先上图片简单了解一下 放大招、慢慢观看 SQL语句执行过程详解 一条sql,plsql的执行到底是怎样执行的呢? 一、SQL语句执行原理: 第一步:客户端把语句发给服务器端执行 当我们在客户端执行 select 语句时,客户端会把这条 SQL 语句发送给服务器端,让服务器端的 进程来处理这语句。也就是说,Oracle 客户端是不会做任何的操作,他

mysql数据库底层原理-爱代码爱编程

一、MySQL 的基本定义: A.术语介绍 1. 数据库(Database) 是按照数据结构来组织、存储和管理数据的仓库。 每个数据库都有一个或多个不同的API用于创建、访问、管理、搜索和复制所保存的数据。 2. RDBMS(Relational Database Management System) 关系数据库管理系统,由瑞典MySQL AB

qt往mysql写入大量数据库_Qt中提高sqlite的读写速度(使用事务一次性写入100万条数据)...-爱代码爱编程

SQLite数据库本质上来讲就是一个磁盘上的文件,所以一切的数据库操作其实都会转化为对文件的操作,而频繁的文件操作将会是一个很好时的过程,会极大地影响数据库存取的速度。例如:向数据库中插入100万条数据,在默认的情况下如果仅仅是执行query.exec("insert into DataBase(......) values(......)");就会打

分析oracle缓慢原因,SQL语句响应缓慢原因及优化-爱代码爱编程

1运行过程可能发生的原因及优化方案 ?  SQL语句的运行过程 1).使用hash算法得到sql语句的hash_value值 2).如果hash_value值在内存中,叫做命中执行软解析 3).如果hash_value值不存在,执行硬解析 4).语法解析,查看是否有错误 5).语意解析,查看权限是否符合 6).若有视图,取出视图的定义

mysql备份表sql语句-爱代码爱编程

前言 从接触编程就开始使用 Git 进行代码管理,先是自己玩 Github,又在工作中使用 Gitlab,虽然使用时间挺长,可是也只进行一些常用操作,如推拉代码、提交、合并等,更复杂的操作没有使用过,看过的教程也逐渐淡忘了,有些对不起 Linus 大神。 出来混总是要还的,前些天就遇到了 Git 里一种十分糟心的场景,并为之前没有深入理解 Git 命令

Oracle SQL语句执行流程与顺序原理详解-爱代码爱编程

以前读的文章,保存到本地了,忘记来源了,分享一下,本地存着怕丢了 Oracle SQL语句执行流程与顺序原理详解 第一步:客户端把语句发给服务器端执行 当我们在客户端执行SQL语句时,客户端会把这条SQL语句发送给服务器端,让服务器端的进程来处理这语句。也就是说,Oracle 客户端是不会做任何的操作,他的主要任务就是把客户端产生的一些SQL语句发送

ue4 unlua源码解析3 -爱代码爱编程

FReflectionRegistry内重要方法逐行解释 RegisterClass