从sql中提取表名称,删除sql中的某个查询条件

本文更详细的介绍可移步:从sql中提取表名称,删除sql中的某个查询条件_苏尔伯特的博客-CSDN博客

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

/**
 * @Author 苏尔伯特
 * @Date 2021/11/20 13:09
 */
public class SqlParser {

    /**
     * 关键词列表,可以根据自己的业务场景添加所需判断的关键词
     * 为了减少不必要的操作,关键词应当越少越好,但也不能过少从而影响判断
     */
    private final List<String> keywordList = new ArrayList<>();

    {
        keywordList.add("select");
        keywordList.add("case");
        keywordList.add("when");
        keywordList.add("then");
        keywordList.add("else");
        keywordList.add("end");
        keywordList.add("join");
        keywordList.add("on");
        keywordList.add("where");
    }

    /*-------------------------------解析出sql中的表名--------------------------------------------*/

    private List<String> getSqlLines(String originSql) {
        String[] lines = originSql.split("\\v");
        List<String> newLines = new ArrayList<>();
        for (String line : lines) {
            String trimLine = line.trim();
            if ("".equals(trimLine) || trimLine.startsWith("--") || trimLine.startsWith("#")) {
                continue;
            }
            newLines.add(trimLine);
        }
        return newLines;
    }

    private String getInlineSql(String originSql) {
        List<String> newLines = getSqlLines(originSql);
        StringBuilder sql = new StringBuilder();
        newLines.forEach(str -> sql.append(str).append(" "));
        return sql.toString();
    }

    public Set<String> getTablesFromSql(String originSql) {
        Set<String> tableSet = new TreeSet<>();
        String sql = getInlineSql(originSql);
        // 首先,表名肯定是跟在这些关键词后面的
        String[] sqlFrag = sql.split("from|join|FROM|JOIN");
        for (int i = 1; i < sqlFrag.length; i++) {
            String trimFrag = sqlFrag[i].trim();
            // 如果有子查询的话,那么from后面就是跟着 '(' 但这不是我们想要的,因此pass
            if (!trimFrag.startsWith("(")) {
                String tableName = trimFrag.split(" ")[0];
                if (tableName.contains("`")) {
                    tableName = tableName.replaceAll("`", "");
                }
                // 这里是将表名前面的限定名去掉了,可以根据自己的需求来
                if (tableName.contains(".")) {
                    tableName = tableName.substring(tableName.indexOf('.') + 1);
                }
                tableSet.add(tableName);
            }
        }
        return tableSet;
    }

    /*-------------------------------删除指定查询条件--------------------------------------------*/

    /**
     * 主逻辑:
     * 1. 匹配关键字
     * 2. 找到了关键字 是->3, 否->6
     * 3. 判断是否在查询条件语句中: 是->4, 否->5
     * 4. 删除该条件,返回删除条件后的sql, 然后继续匹配下一个可能出现的地方 -> 1
     * 5. 从当前关键字之后继续查询下一次出现的位置 -> 1
     * 6. 返回当前sql
     *
     * @param sql           当前sql
     * @param customKeyword 自关键词
     * @param offSet        索引偏移量
     * @return 删除了存在关键字之后的sql
     */
    public String removeCondition(String sql, String customKeyword, int offSet) {
        Matcher matcher = Pattern.compile(customKeyword).matcher(sql);
        if (offSet == 0 ? matcher.find() : matcher.find(offSet)) {
            int customKeywordStartIndex = matcher.start();
            Map<Integer, String> prevKeywordMap = inCondition(sql, customKeywordStartIndex).get(true);
            if (prevKeywordMap != null && !prevKeywordMap.isEmpty()) {
                return removeCondition(removeExecute(sql, customKeywordStartIndex, prevKeywordMap), customKeyword, offSet);
            } else {
                return removeCondition(sql, customKeyword, customKeywordStartIndex + 1);
            }
        } else {
            return sql;
        }
    }

    /**
     * 已知关键字所在位置是在条件中,要删除该关键字所在的查询条件
     *
     * @param sql                给定sql
     * @param customKeywordIndex 自关键词索引
     * @param prevKeywordMap     sql关键词
     * @return 删除了当前关键字索引所在的条件后的sql
     */
    private String removeExecute(String sql, int customKeywordIndex, Map<Integer, String> prevKeywordMap) {
        List<Integer> indexList = currentConditionInfo(sql, customKeywordIndex, prevKeywordMap);
        StringBuilder sqlBuilder = new StringBuilder(sql);
        StringBuilder newSql = sqlBuilder.delete(indexList.get(0), indexList.get(1));
        return newSql.toString();
    }

    /**
     * 获取当前位置所在条件语句的信息
     *
     * @param sql                给定sql
     * @param customKeywordIndex 自关键词索引
     * @param prevKeywordMap     sql关键词Map
     * @return indexList: [0]——表示起始索引 [1]——表示结束索引
     */
    private List<Integer> currentConditionInfo(String sql, int customKeywordIndex, Map<Integer, String> prevKeywordMap) {
        List<Integer> indexList = new ArrayList<>(2);
        int keyOfSingleMap = keyOfSingleMap(prevKeywordMap);
        String sqlKeyword = prevKeywordMap.get(keyOfSingleMap);
        int prevAndIndex = prevAndIndex(sql, customKeywordIndex, sqlKeyword);
        int nextAndIndex = nextAndIndex(sql, customKeywordIndex, sqlKeyword);

        // 根据前一个and和后一个and的索引【它们不一定都存在】判断出当前条件语句的起末位置
        if (prevAndIndex != 0) {
            indexList.add(0, prevAndIndex);
            if (nextAndIndex != 0) {
                indexList.add(1, nextAndIndex);
            } else {
                // 在where语句中,直接到sql结尾;否则查找下一个sql关键词
                if ("where".equalsIgnoreCase(sqlKeyword)) {
                    indexList.add(1, sql.length());
                } else {
                    Map<Integer, String> nextKeywordMap = nextKeywordMap(sql, customKeywordIndex);
                    int nextKeywordIndex = keyOfSingleMap(nextKeywordMap);
                    indexList.add(1, nextKeywordIndex);
                }
            }
        } else {
            // 当前条件语句是查询语句中第一个条件
            if (nextAndIndex != 0) {
                // 改条件语句后面还有其他条件语句
                indexList.add(0, keyOfSingleMap + sqlKeyword.length());
                indexList.add(1, nextAndIndex + 3);
            } else {
                // 该条件语句是唯一的条件语句,where语句整个删除,join语句不做操作
                if ("where".equalsIgnoreCase(sqlKeyword)) {
                    indexList.add(0, keyOfSingleMap);
                    indexList.add(1, sql.length());
                }
            }
        }
        return indexList;
    }

    /**
     * 获取当前条件语句中前一个 and 关键词的索引
     */
    private int prevAndIndex(String sql, int customKeywordIndex, String sqlKeyword) {
        // 往前查询,直到sql关键词之处。若存在”and“,说明这不是第一个条件语句,否则这是第一个条件语句
        Map<Integer, String> prevWordMap = prevWordMap(sql, customKeywordIndex);
        int prevWordIndex = prevWordMap.keySet().iterator().next();
        int prevAndIndex = 0;
        while (!sqlKeyword.equalsIgnoreCase(prevWordMap.get(prevWordIndex))) {
            if ("and".equalsIgnoreCase(prevWordMap.get(prevWordIndex))) {
                prevAndIndex = prevWordIndex;
                break;
            } else {
                prevWordMap = prevWordMap(sql, prevWordIndex);
                prevWordIndex = prevWordMap.keySet().iterator().next();
            }
        }
        return prevAndIndex;
    }

    /**
     * 获取当前条件语句中下一个 and 关键词的索引
     */
    private int nextAndIndex(String sql, int customKeywordIndex, String sqlKeyword) {
        // 往后查询,如果当前是在join语句中,则设定循环截至到where关键词;否则循环截至到最后。判断这里面有没有出现“and”
        Map<Integer, String> nextWordMap = nextWordMap(sql, customKeywordIndex);
        int nextWordIndex = keyOfSingleMap(nextWordMap);
        int nextAndIndex = 0;
        while (nextWordIndex < sql.length()) {
            if ("and".equalsIgnoreCase(nextWordMap.get(nextWordIndex))) {
                nextAndIndex = nextWordIndex;
                break;
            } else {
                nextWordMap = nextWordMap(sql, nextWordIndex);
                nextWordIndex = keyOfSingleMap(nextWordMap);
            }
            // join语句中则以下一个where关键词为截止点
            if ("on".equalsIgnoreCase(sqlKeyword) && "where".equalsIgnoreCase(nextWordMap.get(nextWordIndex))) {
                break;
            }
        }
        return nextAndIndex;
    }

    /**
     * 前一个单词或后一个单词是关系运算符则判定当前索引在条件语句或者case-when语句中
     * 通过前面的第一个关键词,排除其在case-when语句中的情况
     */
    private Map<Boolean, Map<Integer, String>> inCondition(String sql, int currentIndex) {
        Map<Boolean, Map<Integer, String>> result = new HashMap<>(1);
        Map<Integer, String> prevKeywordMap = prevKeywordMap(sql, currentIndex);
        String prevKeyword = valueOfSingleMap(prevKeywordMap);
        if ((isRelationalOperator(prevWord(sql, currentIndex)) || isRelationalOperator(nextWord(sql, currentIndex)))
                && ("join".equalsIgnoreCase(prevKeyword) || "on".equalsIgnoreCase(prevKeyword) || "where".equalsIgnoreCase(prevKeyword))) {
            result.put(true, prevKeywordMap);
        }
        return result;
    }

    /**
     * 当前”单词“是否是关系运算符
     * 其他复杂的情况,包括大小写问题可自行扩展
     */
    private boolean isRelationalOperator(String word) {
        return word.contains("=")
                || word.contains(">")
                || word.contains("<")
                || word.contains("between")
                || word.contains("and")
                || word.contains("or");
    }

    private String nextWord(String str, int currentIndex) {
        Map<Integer, String> wordMap = nextWordMap(str, currentIndex);
        return valueOfSingleMap(wordMap);
    }

    private String prevWord(String str, int currentIndex) {
        Map<Integer, String> wordMap = prevWordMap(str, currentIndex);
        return valueOfSingleMap(wordMap);
    }

    /**
     * 获取单元素map中的key
     *
     * @param singleMap 单元素Map
     * @return 该map中唯一的key
     */
    private int keyOfSingleMap(Map<Integer, String> singleMap) {
        return singleMap.keySet().iterator().next();
    }

    /**
     * 获取单元素map中的value
     *
     * @param singleMap 单元素Map
     * @return 该map中唯一的value
     */
    private String valueOfSingleMap(Map<Integer, String> singleMap) {
        return singleMap.get(keyOfSingleMap(singleMap));
    }

    private Map<Integer, String> prevKeywordMap(String sql, int currentIndex) {
        Map<Integer, String> wordMap = prevWordMap(sql, currentIndex);
        Integer index = wordMap.keySet().iterator().next();
        if (!keywordList.contains(wordMap.get(index).toLowerCase())) {
            return prevKeywordMap(sql, index);
        } else {
            return wordMap;
        }
    }

    private Map<Integer, String> nextKeywordMap(String sql, int currentIndex) {
        Map<Integer, String> wordMap = nextWordMap(sql, currentIndex);
        Integer index = wordMap.keySet().iterator().next();
        if (!keywordList.contains(wordMap.get(index).toLowerCase())) {
            return nextKeywordMap(sql, index);
        } else {
            return wordMap;
        }
    }

    /**
     * 下一个单词【右侧】
     *
     * @param str          所在语句
     * @param currentIndex 当前单词的起始索引【自左向右】
     * @return 右侧下一个单词的索引和字符串
     */
    private Map<Integer, String> nextWordMap(String str, int currentIndex) {
        int firstIndex;
        int endIndex;
        // 当前索引位置是 ‘ ’则找出下一个单词的起始,否则找到本单词起始位置
        Map<Integer, String> wordMap = new HashMap<>();
        String word;
        if (str.charAt(currentIndex) == ' ') {
            firstIndex = nextWordBeginIndex(str, currentIndex);
        } else {
            endIndex = currentWordEndIndex(str, currentIndex);
            firstIndex = nextWordBeginIndex(str, endIndex + 1);
        }
        endIndex = currentWordEndIndex(str, firstIndex);
        word = str.substring(firstIndex, endIndex + 1);
        wordMap.put(firstIndex, word);
        return wordMap;
    }

    /**
     * 前一个单词【左侧】
     *
     * @param str          所在语句
     * @param currentIndex 当前单词的起始索引【自左向右】
     * @return 左侧前一个单词的索引和字符串
     */
    private Map<Integer, String> prevWordMap(String str, int currentIndex) {
        int firstIndex;
        int endIndex;
        // 当前索引位置是' ', 则找出前一个单词的结尾,否则找到本单词的开头
        Map<Integer, String> wordMap = new HashMap<>();
        String word;
        if (str.charAt(currentIndex) == ' ') {
            endIndex = prevWordEndIndex(str, currentIndex);
        } else {
            firstIndex = currentWordBeginIndex(str, currentIndex);
            endIndex = prevWordEndIndex(str, firstIndex - 1);
        }
        firstIndex = currentWordBeginIndex(str, endIndex);
        word = str.substring(firstIndex, endIndex + 1);
        wordMap.put(firstIndex, word);
        return wordMap;
    }

    private int prevWordEndIndex(String str, int currentIndex) {
        for (int i = currentIndex; i >= 0; i--) {
            if (str.charAt(i) == ' ') {
                continue;
            }
            if (isEndOfWord(str, i)) {
                return i;
            }
        }
        return 0;
    }

    private int currentWordBeginIndex(String str, int currentIndex) {
        for (int i = currentIndex; i >= 0; i--) {
            if (isBeginOfWord(str, i)) {
                return i;
            }
        }
        return 0;
    }

    private int nextWordBeginIndex(String str, int currentIndex) {
        for (int i = currentIndex; i < str.length(); i++) {
            if (str.charAt(i) == ' ') {
                continue;
            }
            if (isBeginOfWord(str, i)) {
                return i;
            }
        }
        return 0;
    }

    private int currentWordEndIndex(String str, int currentIndex) {
        for (int i = currentIndex; i < str.length(); i++) {
            if (isEndOfWord(str, i)) {
                return i;
            }
        }
        return 0;
    }

    /**
     * 当前索引指向单词的最后,但不超过该单词
     */
    private boolean isEndOfWord(String str, int index) {
        if (index == str.length() - 1) {
            return true;
        }
        return (str.charAt(index + 1) == ' ' || str.charAt(index + 1) == ')') && str.charAt(index) != ' ';
    }

    /**
     * 当前索引指向单词的开始,但不超过该单词
     */
    private boolean isBeginOfWord(String str, int index) {
        if (index == 0) {
            return true;
        }
        return (str.charAt(index - 1) == ' ' || str.charAt(index - 1) == '(') && str.charAt(index) != ' ';
    }
}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,651评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,468评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,931评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,218评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,234评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,198评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,084评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,926评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,341评论 1 311
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,563评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,731评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,430评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,036评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,676评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,829评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,743评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,629评论 2 354

推荐阅读更多精彩内容