前言
在"Vibe Coding"(氛围编程)时代,代码开发的平权正在发生,而数据平权(Data Democracy) 也紧随其后。Text2SQL 技术让非技术人员只需通过自然语言描述,就能直接与数据库对话并获取结果。
今天,我们将基于 SQLite 和 DeepSeek API,从零构建一个轻量级的 Text2SQL 系统。本文不仅包含核心代码实现,还会分享在实际开发中遇到的"数据库锁"、"多表 Schema 缺失"以及"事务处理"等实战踩坑经验。
一、 基础环境与数据准备
1.1 为什么选择 SQLite?
正如我们的 readme106.md 笔记中提到的:
作为系统内置的文件型数据库,SQLite 是一个轻量级的数据库,无需安装,无需配置,无需管理。
这让它成为我们验证 Text2SQL 逻辑的最佳沙盒。
1.2 初始化大模型客户端
我们通过 OpenAI SDK 兼容的方式接入 DeepSeek:
python
ini
from openai import OpenAI
client = OpenAI(
api_key="sk-8dbb...", # 你的 DeepSeek API Key
base_url="https://api.deepseek.com/v1"
)
1.3 创建员工表并插入数据
我们创建一个 employees 表,并尝试批量插入数据。
python
python
import sqlite3
conn = sqlite3.connect("test.db")
# 创建一个游标对象,执行sql操作
cursor = conn.cursor()
cursor.execute("""
CREATE TABLE IF NOT EXISTS employees (
id INTEGER PRIMARY KEY,
name TEXT,
department TEXT,
salary INTEGER
)
""")
sample_data = [
(6, "胡航", "销售", 50000),
(7, "牛奶", "工程", 75000),
(8, "倩倩", "销售", 60000),
(9, "月月", "工程", 80000),
(10, "黄仁勋", "市场", 55000),
]
# insertmany 批量插入,提升性能
cursor.executemany(
"INSERT INTO employees VALUES (?,?,?,?)",
sample_data
)
conn.commit()
⚠️ 踩坑提示: 在实际运行 Notebook 时,很容易遇到
OperationalError: database is locked。这是因为 SQLite 默认写锁机制较严格,特别是在 Jupyter 反复执行同一个 Cell 或存在未关闭的连接时。建议在重跑前conn.close(),或者确保每次只运行一次初始化逻辑。
二、 构建 Text2SQL 核心引擎
2.1 提取数据库 Schema
大模型需要知道表结构才能写 SQL。注释中写得非常清楚:Schema 应详细描述每个表的字段和类型,这有助于模型理解表的结构和关联。
python
python
# 获取数据库Schema
# SQLite命令,查看employees表的列名、类型等结构信息。
schema = cursor.execute("PRAGMA table_info(employees)").fetchall()
print(schema)
# 列表推导式
# 使用 f-string(格式化字符串字面量),将列名和类型拼接成一个字符串。
schema_str = "CREATE TABLE EMPLOYEES (\n" + "\n".join([f"{col[1]} {col[2]}" for col in schema]) + "\n)"
print("数据库Schema:")
print(schema_str)
2.2 定义 Prompt 与 DeepSeek 调用
python
ini
# 销售部门平均工资多少?先分组再算平均
# text2sql 数据平权
# vibe coding 平权了代码开发
def ask_deepseek(query, schema):
# f 作为模版
prompt = f"""
这是一个数据库的Schema:
{schema}
根据这个Schema,请输出一个SQL查询来回答以下问题。
只输出SQL 查询语句本身,不要使用任何Markdown格式,
不要包含反引号、代码块标记或者额外说明。
问题:{query}
"""
response = client.chat.completions.create(
model = "deepseek-v4-flash",
max_tokens = 2048,
messages=[{
"role":"user",
"content":prompt
}]
)
return response.choices[0].message.content
三、 CRUD 实战演练
接下来我们看看大模型对于自然语言的 SQL 生成能力。
- 查询操作 :
question = "工程部门员工的姓名和工资是多少?"
模型输出:SELECT name, salary FROM EMPLOYEES WHERE department = '工程';✅ - 插入操作 :
question = "在销售部门增加一个新员工,姓名为张三,工资为45000"
模型输出:INSERT INTO EMPLOYEES (name, department, salary) VALUES ('张三', '销售', 45000);✅ - 更新操作 :
question = "将倩倩的工资调整为55000"
模型输出:UPDATE EMPLOYEES SET salary = 55000 WHERE name = '倩倩';✅ - ❌ 删除操作的"幻觉" :
question = "删除市场部门的黄仁勋"
模型却输出了:DELETE FROM EMPLOYEES WHERE id = 12 AND name = '张三';
分析:大模型在这里产生了严重的幻觉,因为张三是上一个上下文插入的,模型将其与黄仁勋的删除混淆,并错误地估计了id=12。这也告诉我们,生产环境中必须对 Text2SQL 生成的增删改语句进行严格校验或引入 Human-in-the-loop。
四、 进阶:多表查询与事务管理
结合 readme106.md 中的设计,我们要进一步引入 departments 表(包含 id, name, manager)。
4.1 唯一性约束踩坑
在尝试插入部门数据时:
python
python
cursor.execute("""
CREATE TABLE IF NOT EXISTS departments(
id INTEGER PRIMARY KEY,
name TEXT,
manager TEXT
)
""")
sample_departments = [
(1, "销售", "王经理"),
(2, "工程", "李经理"),
(3, "市场", "张经理")
]
# 执行 executemany 时可能会遇到 IntegrityError
如果多次运行,会遇到 IntegrityError: UNIQUE constraint failed: departments.id。这是因为 CREATE TABLE IF NOT EXISTS 不会清空表,主键冲突会导致插入失败。建议使用 INSERT OR IGNORE 或事先 DROP TABLE。
4.2 多表查询的 Schema 盲区
当我们提出需求:question = "根据两个表之间的关系,列出每个部门的员工人数和平均工资"
模型给出:
sql
vbnet
SELECT department, COUNT(*) AS employee_count, AVG(salary) AS average_salary FROM EMPLOYEES GROUP BY department;
🔍 发现问题了吗?
生成的 SQL 只用了 EMPLOYEES 表,根本没有连表(JOIN)departments。这是因为我们在构建 schema_str 时,只提取了 employees 表的 Schema ,模型根本不知道 departments 表的存在!这提醒我们,在做多表 Text2SQL 时,提供全量 Schema 至关重要。
4.3 引入事务(Transaction)
针对 readme106.md 中提到的业务场景:
- 订单
order - 商品
product count-1 - 付款
pay
在 Text2SQL 落地到实际业务时,我们必须引入事务(Transaction) 。单纯让 LLM 生成一句 SQL 是不够的,因为像"生成订单"、"扣减库存"、"付款"往往需要多条 SQL 保持原子性。一旦其中某一步骤失败,必须整体回滚(Rollback),确保数据一致性。
python
php
try:
# 开启事务
cursor.execute("BEGIN TRANSACTION")
# 大模型生成的三条相关SQL
# cursor.execute(sql_order)
# cursor.execute(sql_product)
# cursor.execute(sql_pay)
conn.commit()
except Exception as e:
conn.rollback()
五、 总结与展望
通过这个实战项目,我们验证了 Text2SQL 数据平权 的可行性。大模型在处理标准 CRUD 甚至部分聚合查询(如 GROUP BY)时已经游刃有余。
但在走向生产的过程中,我们依然需要警惕:
- Schema 注入不全:多表查询时模型容易盲目自信,提供完整详尽的 Schema(包括外键关系)是成功的关键。
- 大模型的幻觉:如删除操作的乌龙,对于变更类 SQL 必须谨慎。
- 事务保障:复杂业务链路不能依赖单条 SQL,必须在应用层利用事务兜底。