《SQL必知必会》学习随笔:Text to SQL 与行业实战

前言

专栏在完结多年后又增加了几篇加餐,主要分成两类内容:

  • Text to SQL:使用自然语言生成 SQL;
  • 行业实战:银行、保险、证券、新能源车企和快消场景中的查询与优化。

行业实战部分已经写得很具体,我就不重复搬运原文了。这篇主要整理 Text to SQL 的思路,并补充一些我认为真正落地时必须注意的问题。

Text to SQL 是什么

Text to SQL,简单来说就是把自然语言问题转换成 SQL 查询。

例如用户提出:

查询最近 30 天销量最高的 10 个商品,并显示商品名称和销售额。

模型需要先理解“最近 30 天”“销量最高”“销售额”等业务含义,再结合数据表、字段、关联关系和数据库类型,生成可以执行的 SQL。

它降低了 SQL 的使用门槛,但不意味着完全不需要懂 SQL。特别是涉及复杂查询、权限控制、性能优化和数据修改时,最终仍然需要有人检查生成结果。

不要只看模型榜单

原内容列举了当时常见的闭源模型、开源模型和代码模型。但模型版本更新很快,半年后榜单可能就已经没有参考价值了,所以这里不再按名称排一个容易过时的名次。

如果要选择 Text to SQL 模型,我更关心下面几点:

  1. Schema 理解能力:能否理解表结构、主外键、字段注释和业务含义;
  2. 复杂查询能力:能否正确生成多表连接、聚合、窗口函数和子查询;
  3. 方言支持:是否知道 MySQL、PostgreSQL、SQL Server 等数据库的语法差异;
  4. 结构化输出能力:能否稳定地只返回 SQL 或指定 JSON,而不是夹杂大段说明;
  5. 上下文长度与成本:面对大量表结构时,能否在成本可接受的情况下完成任务;
  6. 本地部署需求:敏感数据是否允许发送到外部服务。

对实际项目来说,用自己的数据库问题做一套测试集,通常比只看公开榜单更可靠。

Text to SQL 的完整流程

如果只是把一句自然语言和整个数据库结构一起丢给模型,简单问题可能能用,但表一多,准确率就会明显下降。

我目前理解的完整流程如下:

  1. 理解用户问题:识别指标、维度、筛选条件、时间范围和排序要求;
  2. 检索相关 Schema:只找与当前问题有关的表和字段,而不是把整个数据库全部塞进去;
  3. 补充业务语义:说明“有效订单”“销售额”“新增用户”等业务概念如何计算;
  4. 生成 SQL:明确数据库方言,并限制输出格式;
  5. 静态校验:检查表名、字段名、语法、危险语句和权限范围;
  6. 试执行或解释执行计划:优先使用只读账号,必要时先执行 EXPLAIN
  7. 根据错误修正:把数据库返回的错误信息交给模型进行有限次数的修复;
  8. 返回结果与说明:除了查询结果,还应说明口径和可能存在的限制。

这套流程中,真正困难的往往不是“写出一条看起来像 SQL 的字符串”,而是找到正确的表、理解业务口径,并保证执行安全。

提示词应该提供什么

原文给出了三种提示词。核心结论是:与一段模糊的中文表说明相比,结构化的建表语句通常能给模型更多信息。

我认为一个比较实用的提示至少应包含:

  • 数据库类型和版本;
  • 相关表的 DDL;
  • 字段注释和枚举含义;
  • 表之间的关联关系;
  • 用户问题;
  • 输出格式;
  • 安全限制;
  • 必要时提供一两个相似示例。

可以写成下面这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
你是一名 SQL 助手,请根据给定的数据库结构生成查询。

数据库方言:MySQL 8.0

要求:
1. 只生成 SELECT 或 WITH 查询;
2. 不允许生成 INSERT、UPDATE、DELETE、DROP、ALTER、TRUNCATE;
3. 只使用提供的表和字段;
4. 无法确定业务口径时先提出问题,不要自行猜测;
5. 最终只在一个 sql 代码块中返回 SQL。

用户问题:
{query}

数据库结构:
```sql
{create_sql}
```

业务说明:
{business_context}

这里的 create_sql 不只是表名列表,最好包含字段类型、主键、外键、唯一约束和注释。因为这些信息能帮助模型判断字段用途,也能减少编造不存在字段的情况。

为什么“只给 DDL”仍然不够

DDL 适合描述数据库结构,但它不一定能表达业务语义。

例如订单表中可能同时存在:

  • created_at:订单创建时间;
  • paid_at:支付时间;
  • finished_at:完成时间;
  • cancelled_at:取消时间。

用户问“本月订单量”时,到底应该使用哪个时间字段?只看 DDL 很难确定。因此在真实项目中,还需要补充指标定义、字段说明,或者让模型在不确定时先追问。

另一个问题是表太多。假设数据库中有几百张表,把所有 DDL 都放进提示词不仅浪费上下文,还会增加模型选错表的概率。更合理的方式是先检索相关表,再生成 SQL,也就是把问题拆成“找表”和“写 SQL”两个阶段。

安全问题比生成能力更重要

Text to SQL 最危险的地方,不是 SQL 写错后报语法错误,而是它能够执行,但查询口径错误、扫描数据过多,甚至修改了不该修改的数据。

我认为至少要做下面几层限制:

使用只读账号

面向查询的 Text to SQL 服务不应该连接拥有写权限或 DDL 权限的数据库账号。即使提示词要求“只生成 SELECT”,也不能把安全完全寄托在模型听话上。

限制语句类型

在执行前解析 SQL,只允许 SELECTWITH 和必要的解释语句。不能只用简单的字符串包含判断,因为注释、大小写和嵌套语法都可能绕过这种检查。

控制查询成本

可以设置超时时间、最大返回行数和资源限制。对可能扫描大量数据的语句,先使用 EXPLAIN 检查执行计划。

记录审计日志

保留用户问题、模型生成的 SQL、执行结果、耗时和错误信息。后续出现问题时,至少能够知道是哪一步出了错。

如何评估生成质量

只看 SQL 能不能执行是不够的。一条 SQL 即使语法正确,也可能回答了另一个问题。

可以从下面几个角度评估:

维度 需要检查的问题
可执行性 SQL 是否能够在目标数据库中执行
结果正确性 返回结果是否符合预期口径
Schema 一致性 是否使用了真实存在的表和字段
安全性 是否包含越权查询或危险操作
性能 是否出现不必要的全表扫描、笛卡尔积或重复子查询
稳定性 同类问题换一种说法后,结果是否仍然正确

如果准备把 Text to SQL 用到正式项目,最好先收集一批真实问题和标准 SQL,做成固定测试集。每次更换模型、提示词或 Schema 检索方式后都重新跑一遍,才能知道效果到底变好了还是变差了。

加餐 02~06:行业查询与优化

这部分主要是原作者从真实开发场景中提炼的经验,针对性很强。我想了一下,原文已经整理得比较精简,我就不班门弄斧了,直接把对应链接列出来,请有需要的朋友自行查看。