自然语言转 SQL 只是 Text2SQL 链路的前半段,真正执行查询前,还需要判断语句是否只读、访问对象是否获准,以及查询会不会带来敏感数据泄露或资源消耗。为此,系统通常会在模型与数据库之间加入 SQL 检查层,而正则表达式和 AST 正是两种常见实现路径。
Text2SQL 的目标,是把自然语言转换成 SQL,再从数据库获取结果。例如用户询问“统计今年每个城市的订单数量”,模型可能生成:
SELECT city, COUNT(*) FROM orders WHERE year = 2026 GROUP BY city
在交给数据库执行前,系统还必须确认:这是只读查询吗?访问的表是否允许访问?返回字段是否敏感?是否包含多条语句、危险函数、异常嵌套或高资源消耗?这类执行前检查通常称为 SQL Guard。
SQL 检查不是 Text2SQL 模型,而是模型和数据库之间的安全、权限、资源和质量控制层。常见实现有两种:基于正则表达式的文本检查,以及基于解析器 AST 的结构化检查。两者都可以工作,选择取决于项目规模、SQL 复杂度、风险等级和维护成本。
AST 是 Abstract Syntax Tree,即“抽象语法树”。它把 SQL 从字符串解析成有层次的结构。
SELECT city, COUNT(*) AS total
FROM orders
WHERE amount > 100
GROUP BY city
可以抽象为:
Select
├── Projection: city, COUNT(*) AS total
├── From: orders
├── Where: amount > 100
└── GroupBy: city
Guard 面对的是查询类型、表节点、字段节点、函数节点和条件节点,而不是一整段难以可靠分析的文本。
DROP、DELETE 或分号。SQL 是有语法结构的语言,正则很难长期承担结构分析职责。
SeLeCt name FROM students
如果规则只匹配大写 SELECT,就会误判;支持大小写后,还要处理换行、注释和方言差异。
SELECT 'DROP TABLE students' AS example FROM students
这里的 DROP TABLE 是字符串内容,不是实际执行的语句,简单搜索关键字会产生误报。
JOIN、别名、子查询和 EXISTS 会形成作用域。正则需要自己处理这些语义,容易漏表、错认别名或把字符串内容当成字段。
SELECT * FROM orders
SELECT COUNT(*) FROM orders
两者都含有 *,但前者返回所有字段,后者只是计数。正则可以找到字符,却很难稳定判断它所在的语法节点。
随着 CTE、窗口函数、子查询、集合运算和数据库方言增加,正则往往变成难以维护的补丁集合。
SELECT *、禁止危险函数、只允许登记表和字段。SQL_MULTI_STATEMENT、SQL_TABLE_NOT_PUBLISHED、SQL_WILDCARD_PROJECTION 等稳定原因码,指导模型重试和管理员排查。student_id,但不知道这个字段是否代表当前登录用户;业务权限仍需要目录、行级策略和服务端上下文。没有一种方式适合所有 Text2SQL 项目。可以从以下维度判断:
| 场景 | 更适合的方式 | 原因 |
|---|---|---|
| 个人项目、Demo、SQL 模板固定 | 正则表达式 | 实现快,依赖少,维护成本低 |
| 只支持简单单表 SELECT | 正则表达式 | 语法范围小,规则容易覆盖 |
| 需要识别 JOIN、子查询、聚合 | AST | 结构信息更准确 |
| 需要表级、字段级权限 | AST | 可以直接遍历表节点和列节点 |
| 支持多种数据库方言 | 视解析器支持情况决定 | AST 更结构化,但需要确认方言兼容性 |
| 高风险生产系统 | AST 或正则 + AST | 通常需要更细粒度的结构检查 |
| 已有成熟正则规则且变更很少 | 正则表达式 | 迁移收益可能不足以抵消改造成本 |
小项目不必为了使用 AST 而引入复杂依赖;复杂项目也不必因为 AST 更先进就完全重写已有规则。可以先用正则满足基础需求,再对复杂 SQL 增加 AST 检查。
自然语言问题
↓
模型生成候选 SQL
↓
长度和字符级预过滤
↓
AST 解析
↓
检查语句类型、表、字段、函数和查询形状
↓
匹配数据目录与业务权限
↓
设置超时、最大行数和最大返回字节数
↓
参数化执行
↓
结果脱敏、审计和回答生成
可以只使用其中一种,也可以组合使用。组合时,正则负责快速处理明显异常输入,AST 负责复杂结构检查;但组合会增加依赖和维护成本,应根据实际收益决定。
假设目录只开放 orders 表的 city、amount 和 created_at 字段。
合法查询:
SELECT city, COUNT(*) AS total
FROM orders
WHERE created_at >= ?
GROUP BY city
Guard 可以确认:它是单条 SELECT,访问已登记表,投影字段已登记,使用允许的聚合函数,时间值通过参数绑定。
应拒绝的查询包括:
SELECT * FROM orders
SELECT city FROM internal_salary
SELECT load_file('/etc/passwd')
SELECT city FROM orders; DELETE FROM orders
对应原因分别是通配符投影、未登记表、危险函数和多语句写操作。
AST 不会直接让模型更聪明,但会减少错误 SQL 进入数据库,并让错误更容易被模型修正:
SELECT * 导致敏感字段泄露或上下文过长;Guard 不能替代 Text2SQL 评测,仍需关注执行准确率、结果准确率、语义等价性、延迟和安全拒绝率。
正则适合判断输入长度、过滤控制字符、做日志脱敏、在 AST 解析前拒绝明显异常输入,以及处理完全固定的模板和简单项目的基础 SQL 检查。
当 SQL 开始包含大量 JOIN、子查询、复杂聚合,或者需要细粒度表字段权限时,正则维护成本会明显上升,此时可以评估引入 AST。
AST 适合需要理解 SQL 结构的场景,例如复杂 JOIN、子查询、聚合、函数检查、表字段权限和多种拒绝原因。引入前应确认解析器对目标数据库方言的支持情况,并评估依赖、性能和规则维护成本。
正则和 AST 都可以用于 Text2SQL 的 SQL 检查:正则的优势是简单、快速、低成本;AST 的优势是结构准确、可解释、可扩展。小项目可以优先考虑正则,复杂项目可以选择 AST,也可以逐步组合两者。
比较稳妥的架构是:
正则表达式或 AST
+ 数据目录与业务权限
+ 参数化执行 + 数据库只读权限 + 结果脱敏与审计
Text2SQL 的目标不是尽可能执行模型生成的 SQL,而是在项目可接受的成本和安全边界内,执行足够准确、可解释、可审计的查询。