Java手写一个简单的sql解析执行器

一,实现步骤

实现一个简单的 SQL 查询引擎需要完成以下几个主要功能:

  1. SQL 解析:将用户输入的 SQL 语句解析成可执行的结构。
  2. 数据存储:存储数据,可以使用内存数据结构(如 HashMapArrayList)模拟数据库表。
  3. 查询执行:根据解析后的 SQL 语句,从数据存储中查询数据。
  4. 结果输出:将查询结果输出给用户。

以下是一个简单的实现示例,支持基本的 SELECT 查询,包括字段选择、表名和简单的 WHERE 条件。

1,数据存储

首先,我们需要一个简单的数据存储结构。这里使用 HashMap 来模拟一个表,其中键是列名,值是列数据。

2,SQL 解析

接下来,实现一个简单的 SQL 解析器,解析 SELECTWHERE 语句。

3,查询执行

根据解析后的 SQL 查询,从数据存储中提取数据。

1,实现数据存储和SQL执行器

上面的一、三两步代码归为一处,数据存储和SQL执行器

package com.example.demo.controller;

import java.util.*;

/**
 * 数据存储
 * 首先,我们需要一个简单的数据存储结构。这里使用 HashMap 来模拟一个表,其中键是列名,值是列数据。
 */
public class SimpleSQLQueryEngine {
    // 模拟数据库表
    private Map<String, List<String>> table;

    public SimpleSQLQueryEngine() {
        table = new HashMap<>();
    }

    // 向表中添加数据
    public void addColumn(String columnName, List<String> data) {
        table.put(columnName, data);
    }

    // 获取表结构
    public Map<String, List<String>> getTable() {
        return table;
    }
    //SQL执行器
    public List<Map<String, String>> executeQuery(SQLParser.SQLQuery query) {
        String[] fields = query.getSelectFields().split(",\\s*");
        String tableName = query.getTableName();
        String whereCondition = query.getWhereCondition();

        List<Map<String, String>> results = new ArrayList<>();

        // 遍历每一行数据
        int rowCount = table.values().iterator().next().size();
        for (int i = 0; i < rowCount; i++) {
            Map<String, String> row = new HashMap<>();
            boolean matchWhere = true;

            for (String field : fields) {
                if (table.containsKey(field)) {
                    row.put(field, table.get(field).get(i));
                } else {
                    throw new IllegalArgumentException("Column not found: " + field);
                }
            }

            // 检查 WHERE 条件
            if (whereCondition != null && !whereCondition.isEmpty()) {
                String[] conditionParts = whereCondition.split("\\s+");
                if (conditionParts.length != 3) {
                    throw new IllegalArgumentException("Invalid WHERE condition");
                }

                String column = conditionParts[0];
                String operator = conditionParts[1];
                String value = conditionParts[2];

                if (!table.containsKey(column)) {
                    throw new IllegalArgumentException("Column not found in WHERE condition: " + column);
                }

                String cellValue = table.get(column).get(i);
                switch (operator) {
                    case "=":
                        matchWhere = cellValue.equals(value);
                        break;
                    case ">":
                        matchWhere = Integer.parseInt(cellValue) > Integer.parseInt(value);
                        break;
                    case "<":
                        matchWhere = Integer.parseInt(cellValue) < Integer.parseInt(value);
                        break;
                    default:
                        throw new IllegalArgumentException("Unsupported operator: " + operator);
                }
            }

            if (matchWhere) {
                results.add(row);
            }
        }
        return results;
    }
}

2,实现SQL解析器

package com.example.demo.controller;

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

/**
 * SQL 解析
 * 接下来,实现一个简单的 SQL 解析器,解析 SELECT 和 WHERE 语句。
 */
public class SQLParser {

    private static final Pattern SELECT_PATTERN = Pattern.compile(
            "\\bSELECT\\b\\s*(.*?)\\s*\\bFROM\\b\\s*(\\w+)(\\s*\\bWHERE\\b\\s*(.*))?",
            Pattern.CASE_INSENSITIVE
    );

    public static SQLQuery parse(String sql) {
        Matcher matcher = SELECT_PATTERN.matcher(sql.trim());
        if (!matcher.matches()) {
            throw new IllegalArgumentException("Invalid SQL query");
        }

        String selectFields = matcher.group(1).trim();
        String tableName = matcher.group(2).trim();
        String whereCondition = matcher.group(4);

        return new SQLQuery(selectFields, tableName, whereCondition);
    }

    public static class SQLQuery {
        private String selectFields;
        private String tableName;
        private String whereCondition;

        public SQLQuery(String selectFields, String tableName, String whereCondition) {
            this.selectFields = selectFields;
            this.tableName = tableName;
            this.whereCondition = whereCondition;
        }

        public String getSelectFields() {
            return selectFields;
        }

        public String getTableName() {
            return tableName;
        }

        public String getWhereCondition() {
            return whereCondition;
        }
    }
}

3,主程序执行SQL测试

将上述部分组合起来,实现一个简单的 SQL 查询引擎

package com.example.demo.controller;

import java.util.*;

/**
 * 实现一个简单的 SQL 查询引擎需要完成以下几个主要功能:
 *      1,SQL 解析:将用户输入的 SQL 语句解析成可执行的结构。
 *      2,数据存储:存储数据,可以使用内存数据结构(如 HashMap 或 ArrayList)模拟数据库表。
 *      3,查询执行:根据解析后的 SQL 语句,从数据存储中查询数据。
 *      4,结果输出:将查询结果输出给用户。
 * 以下是一个简单的实现示例,支持基本的 SELECT 查询,包括字段选择、表名和简单的 WHERE 条件。
 *
 * 主程序
 * 将上述部分组合起来,实现一个简单的 SQL 查询引擎。
 */
public class Main {
    public static void main(String[] args) {
        // 创建查询引擎
        SimpleSQLQueryEngine engine = new SimpleSQLQueryEngine();

        // 添加数据
        engine.addColumn("name", Arrays.asList("Alice", "Bob", "Charlie"));
        engine.addColumn("age", Arrays.asList("25", "30", "35"));

        // 输入 SQL 查询
        String sql = "SELECT name, age FROM table WHERE age > 25";

        // 解析 SQL
        SQLParser.SQLQuery query = SQLParser.parse(sql);

        // 执行查询
        List<Map<String, String>> results = engine.executeQuery(query);

        // 输出结果
        for (Map<String, String> row : results) {
            System.out.println(row);
        }
    }
}

4,运行结果

对于输入的 SQL 查询

SELECT name, age FROM table WHERE age > 25

程序输出

{name=Bob, age=30}
{name=Charlie, age=35}

5,扩展建议

  1. 支持更多 SQL 语法:目前只支持简单的 SELECTWHERE,可以扩展支持 INSERTUPDATEDELETE 等操作。
  2. 优化性能:对于大数据量,可以考虑使用更高效的数据结构或索引机制。
  3. 错误处理:增加更详细的错误处理和用户提示。
  4. 支持更多数据类型:目前只支持字符串和整数,可以扩展支持浮点数、日期等。

这个简单的提供了一个基础框架,可以根据需求进一步扩展和优化。

二,知识点补充

Pattern.compile 是 Java 中正则表达式库 java.util.regex 的一个重要方法,用于将字符串形式的正则表达式编译成 Pattern 对象。Pattern 对象是正则表达式的编译表示形式,可以用于匹配字符串。

1. Pattern.compile 的基本用法

方法签名

public static Pattern compile(String regex)
  • 参数
    • regex:字符串形式的正则表达式。
  • 返回值
    • 返回一个 Pattern 对象,表示编译后的正则表达式。

常用的正则表达式修饰符

Pattern.compile 还可以接受一个修饰符参数,用于指定正则表达式的匹配模式。常用的修饰符包括:

  • Pattern.CASE_INSENSITIVE:忽略大小写。
  • Pattern.MULTILINE:多行模式,^$ 匹配每一行的开始和结束。
  • Pattern.DOTALL. 匹配任何字符,包括换行符。
  • Pattern.UNICODE_CASE:启用 Unicode 感知的大小写匹配。
public static Pattern compile(String regex, int flags)

2. 示例代码

示例 1:简单匹配

以下代码演示了如何使用 Pattern.compile 来匹配字符串中的数字。

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

public class PatternCompileExample {
    public static void main(String[] args) {
        // 编译正则表达式,匹配数字
        Pattern pattern = Pattern.compile("\\d+");

        // 创建 Matcher 对象,用于匹配字符串
        Matcher matcher = pattern.matcher("Hello 123, this is 456");

        // 查找匹配项
        while (matcher.find()) {
            System.out.println("Found number: " + matcher.group());
        }
    }
}

输出结果

Found number: 123
Found number: 456

示例 2:忽略大小写

以下代码演示了如何使用 Pattern.CASE_INSENSITIVE 修饰符来忽略大小写。

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

public class CaseInsensitiveExample {
    public static void main(String[] args) {
        // 编译正则表达式,忽略大小写
        Pattern pattern = Pattern.compile("hello", Pattern.CASE_INSENSITIVE);

        // 创建 Matcher 对象
        Matcher matcher = pattern.matcher("Hello world");

        // 查找匹配项
        if (matcher.find()) {
            System.out.println("Found match: " + matcher.group());
        }
    }
}

输出结果

Found match: Hello

示例 3:多行模式

以下代码演示了如何使用 Pattern.MULTILINE 修饰符来匹配多行字符串。

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

public class MultilineExample {
    public static void main(String[] args) {
        // 编译正则表达式,多行模式
        Pattern pattern = Pattern.compile("^line", Pattern.MULTILINE);

        // 创建 Matcher 对象
        Matcher matcher = pattern.matcher("line 1\nline 2\nanother line");

        // 查找匹配项
        while (matcher.find()) {
            System.out.println("Found match: " + matcher.group());
        }
    }
}

输出结果

Found match: line
Found match: line

3. PatternMatcher 的常用方法

  • Pattern.compile(String regex):编译正则表达式。
  • Pattern.matcher(CharSequence input):创建一个 Matcher 对象,用于匹配输入字符串。
  • Matcher.find():查找输入字符串中下一个匹配项。
  • Matcher.matches():检查整个输入字符串是否匹配正则表达式。
  • Matcher.group():获取匹配的结果字符串。
  • Matcher.group(int group):获取指定捕获组的匹配结果。

4. 注意事项

  1. 性能优化
    • 如果需要多次匹配同一个正则表达式,建议提前编译 Pattern 对象,而不是每次都调用 Pattern.compile
    • 编译后的 Pattern 对象可以多次复用。
  2. 特殊字符转义
    • 正则表达式中的一些特殊字符(如 .*?() 等)需要使用反斜杠 \ 转义。
    • 如果需要匹配反斜杠本身,需要使用 \\
  3. 异常处理
    • 如果正则表达式语法错误,Pattern.compile 会抛出 PatternSyntaxException。建议在实际应用中捕获并处理该异常。

5. Pattern.compile 总结

Pattern.compile 是 Java 中处理正则表达式的基础方法,通过它可以将字符串形式的正则表达式编译成 Pattern 对象,进而使用 Matcher 对象进行匹配操作。掌握其用法可以方便地实现复杂的字符串匹配和处理逻辑。

6. 上面的正则表达式逐步解析

这句代码的作用是使用 Java 的正则表达式库 java.util.regex 来编译一个正则表达式模式,用于匹配简单的 SQL SELECT 查询语句。它通过正则表达式提取查询中的关键部分,如 SELECT 字段、FROM 表名和可选的 WHERE 条件。同时,它还设置了 Pattern.CASE_INSENSITIVE 标志,使匹配过程忽略大小写。

让我们逐步解析这句代码:

1. 正则表达式部分

"\\bSELECT\\b\\s*(.*?)\\s*\\bFROM\\b\\s*(\\w+)(\\s*\\bWHERE\\b\\s*(.*))?"

1.1 \bSELECT\b

  • \b 是单词边界(word boundary)的正则表达式符号,表示匹配单词的开始或结束。
  • SELECT 是要匹配的单词。
  • 这部分的作用是确保匹配到独立的单词 SELECT,而不是单词的一部分(例如 SELECTED)。

1.2 \s*

  • \s 表示匹配任意空白字符(包括空格、制表符、换行符等)。
  • * 表示匹配前面的字符(这里是空白字符)0次或多次。
  • 这部分的作用是允许在 SELECT 和后面的字段之间有任意数量的空白字符。

1.3 (.*?)

  • () 是捕获组(capturing group),用于捕获匹配的部分,以便后续可以通过 Matcher.group() 方法提取出来。
  • . 表示匹配任意单个字符(除换行符外)。
  • * 表示匹配前面的字符(这里是任意字符)0次或多次。
  • ? 表示非贪婪匹配(non-greedy),即尽可能少地匹配字符。
  • 这部分的作用是捕获 SELECTFROM 之间的字段列表(例如 name, age)。

1.4 \bFROM\b

  • \bSELECT\b 类似,匹配独立的单词 FROM

1.5 \s*(\w+)

  • \s* 同前面的解释,匹配任意数量的空白字符。
  • (\w+)
    • () 是捕获组。
    • \w 表示匹配任意字母、数字或下划线(即单词字符)。
    • + 表示匹配前面的字符(这里是单词字符)1次或多次。
  • 这部分的作用是捕获 FROM 后面的表名(例如 users)。

1.6 (\s*\bWHERE\b\s*(.*))?

  • () 是捕获组。
  • ? 表示前面的捕获组是可选的,即 WHERE 条件可以不存在。
  • \s*\bWHERE\b\s*
    • 匹配任意数量的空白字符,然后是单词 WHERE,再后面是任意数量的空白字符。
  • (.*?)
    • 捕获 WHERE 后面的条件部分(例如 age > 20)。
  • 这部分的作用是捕获可选的 WHERE 条件。

2. Pattern.CASE_INSENSITIVE

  • Pattern.CASE_INSENSITIVE 是一个修饰符,表示在匹配过程中忽略大小写。
  • 这意味着正则表达式会匹配 SELECTselectSeLeCt 等所有大小写变体。

3. 代码的作用

这句代码的作用是编译一个正则表达式模式,用于解析简单的 SQL SELECT 查询语句。它能够提取以下部分:

  • SELECT 后面的字段列表(例如 name, age)。
  • FROM 后面的表名(例如 users)。
  • 可选的 WHERE 条件(例如 age > 20)。

4. 示例

假设有一个 SQL 查询:

SELECT name, age FROM users WHERE age > 20

使用这个正则表达式匹配后,可以提取出以下内容:

  • 第一个捕获组 (.*?)name, age
  • 第二个捕获组 (\w+)users
  • 第三个捕获组 (.*?)age > 20

5. 使用示例

以下是一个简单的使用示例:

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

public class SQLParserExample {
    public static void main(String[] args) {
        String sql = "SELECT name, age FROM users WHERE age > 20";
        Pattern pattern = Pattern.compile(
            "\\bSELECT\\b\\s*(.*?)\\s*\\bFROM\\b\\s*(\\w+)(\\s*\\bWHERE\\b\\s*(.*))?",
            Pattern.CASE_INSENSITIVE
        );

        Matcher matcher = pattern.matcher(sql);
        if (matcher.matches()) {
            System.out.println("SELECT fields: " + matcher.group(1)); // 输出: name, age
            System.out.println("FROM table: " + matcher.group(2));   // 输出: users
            System.out.println("WHERE condition: " + matcher.group(4)); // 输出: age > 20
        } else {
            System.out.println("Invalid SQL query");
        }
    }
}

6. 正则表达式解析总结

这句代码通过正则表达式解析简单的 SQL SELECT 查询语句,提取关键部分(字段、表名、条件),并忽略大小写。它在实现简单的 SQL 解析器时非常有用。

7. Matcher.group() 的用法

在 Java 的正则表达式中,Matcher.group() 方法用于获取匹配结果中的捕获组(capturing group)内容。捕获组是正则表达式中用圆括号 () 包裹的部分,用于提取匹配的子字符串。

方法签名

public String group(int group)
  • 参数
    • group:捕获组的索引。0 表示整个匹配的字符串,1 表示第一个捕获组,2 表示第二个捕获组,依此类推。
  • 返回值
    • 返回指定捕获组匹配的子字符串。如果没有匹配到,返回 null

特殊情况

  • 如果调用 group() 方法时不传参数(即 group()),等同于调用 group(0),表示获取整个匹配的字符串。

示例代码

以下是一个完整的示例,展示如何使用 Matcher.group() 方法提取正则表达式中的捕获组内容。

示例代码

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

public class MatcherGroupExample {
    public static void main(String[] args) {
        // 定义正则表达式
        String regex = "\\bSELECT\\b\\s*(.*?)\\s*\\bFROM\\b\\s*(\\w+)(\\s*\\bWHERE\\b\\s*(.*))?";
        // 创建 Pattern 对象
        Pattern pattern = Pattern.compile(regex, Pattern.CASE_INSENSITIVE);

        // 待匹配的字符串
        String sql = "SELECT name, age FROM users WHERE age > 20";

        // 创建 Matcher 对象
        Matcher matcher = pattern.matcher(sql);

        // 检查是否匹配
        if (matcher.matches()) {
            // 获取整个匹配的字符串(group 0)
            System.out.println("Entire match: " + matcher.group(0));

            // 获取第一个捕获组(SELECT 字段)
            System.out.println("SELECT fields: " + matcher.group(1));

            // 获取第二个捕获组(FROM 表名)
            System.out.println("FROM table: " + matcher.group(2));

            // 获取第三个捕获组(WHERE 条件)
            System.out.println("WHERE condition: " + matcher.group(4));
        } else {
            System.out.println("No match found");
        }
    }
}

输出结果

Entire match: SELECT name, age FROM users WHERE age > 20
SELECT fields: name, age
FROM table: users
WHERE condition: age > 20

详细解释

  1. matcher.group(0)
    • 获取整个匹配的字符串,即整个 SQL 查询语句。
    • 在这个例子中,matcher.group(0) 返回 "SELECT name, age FROM users WHERE age > 20"
  2. matcher.group(1)
    • 获取第一个捕获组的内容,即 SELECTFROM 之间的字段部分。
    • 在这个例子中,matcher.group(1) 返回 "name, age"
  3. matcher.group(2)
    • 获取第二个捕获组的内容,即 FROM 后面的表名。
    • 在这个例子中,matcher.group(2) 返回 "users"
  4. matcher.group(4)
    • 获取第四个捕获组的内容,即 WHERE 后面的条件部分。
    • 在这个例子中,matcher.group(4) 返回 "age > 20"

注意事项

  1. 捕获组的索引从 1 开始
    • group(0) 表示整个匹配的字符串。
    • group(1) 表示第一个捕获组,group(2) 表示第二个捕获组,依此类推。
  2. 捕获组可能为空
    • 如果某个捕获组没有匹配到任何内容,group() 方法会返回 null 或空字符串,具体取决于正则表达式的设计。
    • 例如,如果 SQL 查询中没有 WHERE 条件,matcher.group(4) 将返回 null
  3. Matcher.find()Matcher.matches() 的区别
    • Matcher.matches():尝试匹配整个输入字符串。
    • Matcher.find():尝试在输入字符串中查找下一个匹配项。
    • 在使用 Matcher.find() 时,每次调用 find() 都会移动匹配器的游标,因此可以多次调用 group() 方法来提取多个匹配结果。

总结

Matcher.group() 方法是 Java 正则表达式中非常重要的工具,用于提取匹配的子字符串。通过指定捕获组的索引,可以方便地获取正则表达式中定义的各个部分的内容。

posted @ 2025-04-15 18:02  修电脑的  阅读(233)  评论(0)    收藏  举报