SQL AST(Abstract Syntax Tree,抽象语法树)
SQL AST是 SQL 语句的结构化树状表示,它抽象了 SQL 的语法结构,忽略具体词汇细节(如空格、大小写、分号),专注于表达 SQL 的逻辑层次关系。它是数据库引擎、SQL 分析工具的核心数据结构。
SQL AST 的核心特点
| 特性 | 说明 |
|---|---|
| 层级结构 | 根节点表示完整 SQL 语句,子节点分解为子句(SELECT/FROM/WHERE 等) |
| 类型化节点 | 每个节点有明确类型(如 SelectStatement, Column, Join) |
| 上下文无关 | 不依赖具体数据库方言(如 MySQL/Oracle),只描述通用逻辑结构 |
| 忽略冗余细节 | 省略分号、多余空格、注释等非逻辑元素 |
| 可遍历性 | 可通过 Visitor 模式递归遍历所有节点 |
SQL AST 的生成与作用
生成过程

作用
-
SQL 解析
-
数据库引擎:解析 SQL 生成执行计划(如 PostgreSQL 的
pg_parse) -
工具库:JSqlParser/SQLGlot 等库将 SQL 字符串 → AST
-
-
SQL 重写
-
优化查询:改写条件表达式、子查询扁平化
-
安全审计:转义敏感字段、注入检测
-
-
跨数据库迁移
-
通过 AST 中转实现 SQL 方言转换(如 MySQL → PostgreSQL)
-
-
代码生成
-
ORM 框架:将对象操作翻译为 SQL(如 Hibernate)
-
BI 工具:可视化拼接 SQL → 生成 AST → 输出 SQL
-
SQL AST 节点结构示例
以 SELECT id, name FROM users WHERE age > 30 为例:


节点类型详解
| 节点类型 | 示例 | 说明 |
|---|---|---|
SelectStatement | 根节点 | 表示整个 SELECT 语句 |
SelectClause | SELECT id, name | 选择列部分 |
Column | id, name | 列标识符 |
FromClause | FROM users | 数据源部分 |
Table | users | 表标识符 |
WhereClause | WHERE age > 30 | 条件过滤部分 |
BinaryExpression | age > 30 | 二元操作表达式 |
NumberLiteral | 30 | 数字字面量 |
如何使用
官网:How to use it - JSQLParser 5.3 documentation
1.引入jar
jdk17使用5.x版本 ,jdk1.8使用4.x版本
<dependency> <groupId>com.github.jsqlparser</groupId> <artifactId>jsqlparser</artifactId> <version>5.3</version> </dependency>
2.代码示例
例如:以AOP形式 改写 SQL语句
package interceptor;
import holder.SqlHolder;
import holder.TableRenameVisitor;
import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.schema.Table;
import net.sf.jsqlparser.statement.Statement;
import net.sf.jsqlparser.statement.insert.Insert;
import net.sf.jsqlparser.statement.select.PlainSelect;
import net.sf.jsqlparser.statement.select.Select;
import net.sf.jsqlparser.statement.select.SelectItem;
import net.sf.jsqlparser.statement.update.Update;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;
import java.sql.Connection;
import java.util.List;
import java.util.Properties;
@Intercepts(value = {
@Signature(type = StatementHandler.class,method = "prepare",args = {Connection.class, Integer.class})
})
public class SharedInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
SqlHolder sqlHolder = new SqlHolder();
//此处省略业务逻辑
String newSql = sqlHolder.appendShardingKey("","");
return invocation.proceed();
}
@Override
public Object plugin(Object target) {
return Interceptor.super.plugin(target);
}
@Override
public void setProperties(Properties properties) {
Interceptor.super.setProperties(properties);
}
//简单示例
public static void main(String[] args) throws JSQLParserException {
SqlHolder sqlHolder = new SqlHolder();
// INSERT 示例
Statement stmt = CCJSqlParserUtil.parse("INSERT INTO users (id, name) VALUES (1, 'Alice')");
if (stmt instanceof Insert) {
Insert insert = (Insert) stmt;
Table table = insert.getTable();
System.out.println("Table: " + table.getName()); // users
}
// UPDATE 示例
stmt = CCJSqlParserUtil.parse("UPDATE users SET name='Bob' WHERE id=1");
if (stmt instanceof Update) {
Update update = (Update) stmt;
System.out.println("Table: " + update.getTable().getName()); // users
}
// SELECT 示例
String sql = "SELECT id, name FROM users WHERE age > 30";
Select select = (Select) CCJSqlParserUtil.parse(sql);
PlainSelect plainSelect = (PlainSelect) select.getSelectBody();
// 获取查询列(如果为 函数、子查询、表达式、所有列、表的所有列,可再次解析)
List<SelectItem<?>> selectItems = plainSelect.getSelectItems();
System.out.println("selectItems:"+ selectItems.toString());
select.getSelectBody().accept(new TableRenameVisitor("users", "clients"));
System.out.println("Modified SQL: " + select);
String resSql = sqlHolder.appendShardingKey(sql, "");
System.out.println("Modified resSql: " + resSql);
}
}
package holder;
import net.sf.jsqlparser.expression.ExpressionVisitorAdapter;
import net.sf.jsqlparser.schema.Table;
import net.sf.jsqlparser.statement.select.*;
public class TableRenameVisitor extends SelectVisitorAdapter {
private String oldName;
private String newName;
public TableRenameVisitor(String oldName, String newName) {
this.oldName = oldName;
this.newName = newName;
}
@Override
public void visit(PlainSelect plainSelect) {
// 修改主表名
FromItem fromItem = plainSelect.getFromItem();
// 此处为from table ,如果from 子查询 ,需要递归处理
if (fromItem instanceof Table) {
Table table = (Table) fromItem;
if (table.getName().equalsIgnoreCase(oldName)) {
table.setName(newName);
}
}
// 修改 JOIN 表名
if (plainSelect.getJoins() != null) {
for (Join join : plainSelect.getJoins()) {
FromItem joinItem = join.getRightItem();
if (joinItem instanceof Table) {
Table joinTable = (Table) joinItem;
if (joinTable.getName().equals(oldName)) {
joinTable.setName(newName);
}
}
}
}
}
}
package holder;
import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.schema.Table;
import net.sf.jsqlparser.statement.Statement;
import net.sf.jsqlparser.statement.delete.Delete;
import net.sf.jsqlparser.statement.insert.Insert;
import net.sf.jsqlparser.statement.select.FromItem;
import net.sf.jsqlparser.statement.select.PlainSelect;
import net.sf.jsqlparser.statement.select.Select;
import net.sf.jsqlparser.statement.update.Update;
public class SqlHolder {
public String appendShardingKey(String sql,String value) throws JSQLParserException {
Statement statement = CCJSqlParserUtil.parse(sql);
if (statement instanceof Select){
return parseSelectStatement(statement,value);
}else if (statement instanceof Delete){
return parseDeleteStatement(statement,value);
}else if (statement instanceof Insert){
return parseInsertStatement(statement,value);
}else if (statement instanceof Update){
return parseUpdateStatement(statement,value);
}
return "";
}
private String parseUpdateStatement(Statement statement, String value) {
Update update= (Update) statement;
//省略业务修改逻辑
return update.toString();
}
private String parseInsertStatement(Statement statement, String value) {
Insert insert = (Insert) statement;
//省略业务修改逻辑
return insert.toString();
}
private String parseDeleteStatement(Statement statement, String value) {
Delete delete = (Delete) statement;
//省略业务修改逻辑
return delete.toString();
}
private String parseSelectStatement(Statement statement, String value) throws JSQLParserException {
Select select= (Select) statement;
//省略业务修改逻辑
PlainSelect plainSelect = (PlainSelect) select.getSelectBody();
Expression where = plainSelect.getWhere();
// 添加新条件: AND status = 'active'
Expression newCondition = CCJSqlParserUtil.parseCondExpression("status = 'active'");
plainSelect.setWhere(new AndExpression(where, newCondition));
return select.toString();
}
}
&spm=1001.2101.3001.5002&articleId=149030894&d=1&t=3&u=803db64601a3409b9221b6a94c2828eb)
484

被折叠的 条评论
为什么被折叠?



