一聚教程网:一个值得你收藏的教程网站

最新下载

热门教程

DeepSeek 实战:从零构建 Text2SQL 数据平权应用

时间:2026-09-16 12:14:01 编辑:袖梨 来源:一聚教程网

让非技术人员直接用自然语言查询数据库,是 Text2SQL 最直观的应用价值,但从生成一条可执行 SQL 到构建可靠的数据应用,中间仍有不少工程问题。下面将使用 DeepSeek API 和 SQLite 完成一个轻量原型,并重点分析 Schema 提供、写操作校验、多表查询与事务一致性等关键环节。

前言

在“Vibe Coding”(氛围编程)时代,代码开发的平权正在发生,而数据平权(Data Democracy) 也紧随其后。Text2SQL 技术让非技术人员只需通过自然语言描述,就能直接与数据库对话并获取结果。

今天,我们将基于 SQLite 和 DeepSeek API,从零构建一个轻量级的 Text2SQL 系统。本文不仅包含核心代码实现,还会分享在实际开发中遇到的“数据库锁”、“多表 Schema 缺失”以及“事务处理”等实战踩坑经验。


一、 基础环境与数据准备

1.1 为什么选择 SQLite?

正如我们的 readme106.md 笔记中提到的:

作为系统内置的文件型数据库,SQLite 是一个轻量级的数据库,无需安装,无需配置,无需管理。

这让它成为我们验证 Text2SQL 逻辑的最佳沙盒。

1.2 初始化大模型客户端

我们通过 OpenAI SDK 兼容的方式接入 DeepSeek:

python

from openai import OpenAI
client = OpenAI(
    api_key="sk-8dbb...", # 你的 DeepSeek API Key
    base_url="https://api.deepseek.com/v1"
)

1.3 创建员工表并插入数据

我们创建一个 employees 表,并尝试批量插入数据。

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

# 获取数据库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

# 销售部门平均工资多少?先分组再算平均
# 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 表(包含 idnamemanager)。

4.1 唯一性约束踩坑

在尝试插入部门数据时:

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

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

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)时已经游刃有余。

但在走向生产的过程中,我们依然需要警惕:

  1. Schema 注入不全:多表查询时模型容易盲目自信,提供完整详尽的 Schema(包括外键关系)是成功的关键。
  2. 大模型的幻觉:如删除操作的乌龙,对于变更类 SQL 必须谨慎。
  3. 事务保障:复杂业务链路不能依赖单条 SQL,必须在应用层利用事务兜底。

热门栏目