最近很想玩一玩大模型 + 数据库这套东西,于是干脆写了个简单的代码:
我只说人话,让大模型替我写 SQL,我只负责把 SQL 扔进数据库执行,拿结果。
结果发现,这东西不仅好玩,而且对学习 SQL、理解大模型在业务里的用法,特别有帮助。下面就用尽量“学习者视角”,把这个小玩具拆开讲一遍。
1. 这个小项目到底想解决什么问题?
目标就一句话:
不懂 SQL 的人,也能查库、看数据。
用起来的体验大概是这样:
-
我输入一句话:
“工程部门员工的姓名和工资是多少?” -
程序内部悄悄干三件事:
- 看看数据库长啥样(表结构)
- 把“表结构 + 我的问题”丢给大模型,让它生成 SQL
- 把这个 SQL 扔回数据库执行,拿到查询结果
整个过程中,我不需要手写 SQL,只需要会说人话。
对我这种“还在学习阶段的人”来说,这个过程本身就已经是一个非常好的练习:
我一边看 AI 生成的 SQL,一边对照自己的理解,反而倒过来在学 SQL 的写法。
2. 先来一张最简单的员工表,方便练手
为了让事情简单,我只建了一张员工表,字段也非常直白:
id:员工编号name:姓名department:部门salary:工资
用任意一个轻量数据库都行,比如 SQLite。插一点样例数据:工程、销售、市场都有几个人,工资随便写几个不同的数,方便做各种查询、插入、删除的练习。
有了这么一张“小表”,就够我们玩一整套「自然语言 → SQL → 执行 → 结果」的流程了。
3. 先让模型“认识世界”:生成表结构描述(Schema)
大模型虽然“会写 SQL”,但它完全不知道你这张表里具体有哪些字段。
所以第一步,一定要把表结构喂给它。
在程序里,我大致做了这么一件事(用伪代码表示):
schema_rows = cursor.execute("PRAGMA table_info(employees)").fetchall()
# schema_rows 里是每一列的名字和类型
schema_str = (
"CREATE TABLE EMPLOYEES (\n" +
"\n".join([f"{col_name} {col_type}" for (col_name, col_type) in ...]) +
"\n)"
)
最终得到类似这样的字符串:
这段字符串,就是我们之后给大模型的“世界说明书”。
没有它,大模型就只能猜字段名,极容易胡说八道。
学习点:
- 做 Text-to-SQL 一定记得:表结构(schema)必须显式提供给模型。
- Schema 可以用 SQL 描述形式,模型理解起来更自然。
4. 核心函数:把“人话 + 表结构”交给大模型,换回 SQL
接下来是整个小项目最关键的一步:
写一个函数,给它一个问题 + 表结构,它返回一条 SQL 语句。
伪代码大概长这样:
def ask_llm(query, schema):
prompt = f"""
这是一个数据库的 Schema:
{schema}
根据这个 Schema, 请输出一个 SQL 查询来回答以下问题。
只输出 SQL 查询语句本身,不要使用任何 Markdown 格式,
不要包含反引号、代码块标记或额外说明。
问题:{query}
"""
response = client.chat.completions.create(
model="某个聊天模型",
max_tokens=512,
messages=[{
"role": "user",
"content": prompt
}],
temperature=0, # 稳定输出
)
return response.choices[0].message.content.strip()
这里有几个对我这个“学习者”来说特别有价值的点:
- 把 Schema 写在 prompt 里:模型才能基于真实字段生成 SQL
- 严格限制输出格式:只许返回 SQL,本地代码更好解析,避免混进解释文字
temperature=0:为了让模型尽量“少发挥”,输出更稳定,方便对比和调试
5. 走一遍完整流程:从提问到结果
假设我现在的问题是:
“工程部门员工的姓名和工资是多少?”
逻辑步骤就是:
# 已经有 schema_str 了(前面生成好的那段 CREATE TABLE ...)
question = "工程部门员工的姓名和工资是多少"
# 1. 让模型写 SQL
sql_query = ask_llm(question, schema_str)
print(sql_query)
# 模型可能会返回:
# SELECT name, salary FROM EMPLOYEES WHERE department = '工程';
# 2. 执行 SQL
results = cursor.execute(sql_query).fetchall()
print(results)
# [('宁宁', 75000), ('悦悦', 80000), ('王zc', 10000)]
对我这种正在学习的人来说,最爽的点是:
- 我本来只会模模糊糊说一句:“查工程部门工资啊”
- 模型帮我写出了完整的 SQL
- 我再回头去对着 SQL 反推:
“哦,原来条件是这样写的,字段名这样用,字符串还要加引号”
等于我每问一次问题,就白看一个“标准 SQL 示例”。
6. 不止能查,还能“加人、删人、查全部”
只要 prompt 设计得好,这个思路不仅能查,还能做增删操作。比如:
插入一个新员工
“在销售部门增加一个新员工,姓名为张三,工资为45000”
模型可能给你:
INSERT INTO EMPLOYEES (name, department, salary) VALUES ('张三', '销售', 45000);
你执行一下,再查一下表数据,就能看到“张三”被加进去了。
删除某个员工
“删除市场部门的黄仁勋”
模型可能输出:
DELETE FROM EMPLOYEES WHERE department = '市场' AND name = '黄仁勋';
查询所有人
“查询所有员工的信息”
输出可能是:
SELECT * FROM EMPLOYEES;
对于正在学习的人来说,每一个这样的例子,都是一个非常自然的SQL 练习题 + 参考答案。
7. 玩着玩着,会开始关心“工程化的问题”
一开始我只是抱着“玩玩”的心态做了这个小实验,但做着做着,就会自然而然想到一些更“工程向”的问题,比如:
-
安全性
- 能不能对生成的 SQL 做检查,禁止
DROP TABLE这种危险操作? - 读写要不要分开?只给“查询接口”给非技术同学用?
- 能不能对生成的 SQL 做检查,禁止
-
可控性
- 是不是可以只允许
SELECT,写操作必须人工确认? - 对生成的 SQL 加上
LIMIT,防止一次性查太多数据?
- 是不是可以只允许
-
效率
- Schema 要不要缓存?
- 相似的问题要不要加缓存,避免重复请求模型?
这些问题对我来说,本身就是进一步学习“如何在真实项目里用大模型”的好素材。
哪怕现在做不完、做不精,至少面试或者写学习笔记的时候,能说出自己的思路。
8. 对一个“学习者”来说,这个小项目的意义
站在一个还在学习阶段的视角,这个小实验对我最大的帮助有三点:
- 帮我“反向学 SQL”
自然语言提问 → 看 AI 写出的 SQL → 对照理解语法和写法。 - 真实感受“AI First”的开发方式
不再是写一个“聊天机器人”停在那,而是让模型真正在一个小流程里发挥作用。 - 多了一个好聊又不太卷的小项目
不需要大型架构、复杂前后端,只用一个轻量数据库 + 一个大模型 API,就能搭出完整闭环。
如果你也和我一样,还在学习阶段,对 SQL 和大模型都刚刚入门,这样一个小项目非常适合用来练手:
- 代码量不大
- 成就感很强
- 又刚好踩在“未来会越来越常见”的一个方向上
学的时候不一定要一开始就追求“多高级、多通用”,
先让自己真的把这条链路跑通一遍,就已经是很扎实的一步了。