AIGC 时代,数据库还能怎么玩?从 SQLite 到 Text-to-SQL 的完整实践

66 阅读7分钟

在传统的软件开发中,如果我们想查数据库,往往要自己写 SQL:

SELECT name, salary FROM employees WHERE department = '开发部';

对业务同学、产品同学来说,这门槛不低。
在 AIGC(AI Generated Content)时代,我们可以换一种思路:

“帮我查一下开发部所有员工的姓名和工资。”

然后,把这句话交给大模型,让它自动生成 SQL 并执行。
这就是 Text-to-SQL(自然语言生成 SQL)  的典型场景。

本文会用一个完整但很小的例子,带你从零开始实现:

  • 在本地用轻量级关系型数据库存数据
  • 提取数据库结构(Schema)
  • 调用大模型,根据自然语言自动生成 SQL
  • 最终形成一个“自然语言 → SQL → 查询结果”的闭环

一、从 CRUD 到自然语言:我们要解决什么问题?

先回到最传统的场景:数据库操作的核心是 CRUD

  • C:Create(增)
  • R:Read(查)
  • U:Update(改)
  • D:Delete(删)

这套模式几十年没变,问题在于:

  • 需要掌握 SQL 语法
  • 经常被细节问题绊住(字段名拼错、大小写、类型不匹配等)

而在 AIGC 的帮助下,我们可以把这件事情拆成两部分:

  • 人:用自然语言描述需求
  • 模型:理解需求 + 结合数据库结构 → 生成合法 SQL

也就是说,人不再必须是 “会写 SQL 的操作者”,而是 “提出需求的提问者”。


二、打地基:轻量级关系型数据库 + Python

要做 Text-to-SQL,第一步仍然是要有一个可以操作的数据库。
这里选择的是一个非常轻量级的关系型数据库,它有几个优点:

  • 不需要单独安装数据库服务
  • 一个本地文件就是一个数据库
  • 非常适合 Demo、教学、原型验证

在 Python 中,只需要:

import sqlite3

# 1. 连接数据库(如果文件不存在会自动创建)
conn = sqlite3.connect("test.db")

# 2. 获取“游标”(cursor),相当于操作数据库的句柄
cursor = conn.cursor()

有了连接和游标,我们就可以开始建表、插入数据。

1. 建表:定义一个“员工表”

我们用一个典型的“员工表”来作为示例数据模型:

cursor.execute("""
CREATE TABLE IF NOT EXISTS employees (
    id INTEGER PRIMARY KEY,
    name TEXT,
    department TEXT,
    salary INTEGER
)
""")

这个表包含四个字段:

  • id:主键
  • name:姓名
  • department:部门
  • salary:工资

这已经足够支撑很多常见的业务问题,例如:

  • “开发部有哪些人?”
  • “销售部的平均工资是多少?”
  • “工资最高的是谁?”

2. 插入一些示例数据

接着,我们往表里放入几条记录:

sample_data = [
    (6, "陈h", "开发部", 32000),
    (7, "张三", "销售部", 20000),
    (8, "曹ww", "开发部", 33000),
    (9, "李四", "销售部", 15000)
]

cursor.executemany(
    "INSERT INTO employees VALUES (?, ?, ?, ?)",
    sample_data
)
conn.commit()

现在,我们已经有了一个最小可用的“公司员工数据库”。

三、让数据库“自我介绍”:提取 Schema

要让大模型写 SQL,它必须先知道:

  • 数据库里有哪些表?
  • 每张表有哪些字段?字段类型是什么?

这部分信息,就叫做 Schema(模式/结构)

在这个例子里,我们使用数据库内置命令获取表结构:

schema_info = cursor.execute(
    "PRAGMA table_info(employees)"
).fetchall()

schema_str = "CREATE TABLE EMPLOYEES (\n" + \
    "\n".join([f"{col[1]} {col[2]}" for col in schema_info]) + \
    "\n)"

print("数据库 Schema:")
print(schema_str)

打印出来大致类似:

数据库 Schema:
CREATE TABLE EMPLOYEES (
id INTEGER
name TEXT
department TEXT
salary INTEGER
)

这里有几个关键点:

  • 把结构整理成接近 SQL 的描述字符串,便于大模型理解
  • 统一使用大写表名 EMPLOYEES,避免大小写混乱
  • 这份 Schema 会作为 Prompt 的一部分喂给大模型

可以把这一步理解为:

在让大模型写 SQL 之前,先让数据库做一个“自我介绍”。

四、接入大模型:设计一个专门生成 SQL 的 Prompt

接下来,就是把自然语言问题和 Schema 一起交给大模型,让它产出 SQL。

这里使用的是兼容 OpenAI 接口的大模型 Python SDK,大致初始化如下(注意:实际项目中请用环境变量管理密钥):

from openai import OpenAI

client = OpenAI(
    api_key="YOUR_API_KEY",
    base_url="https://api.deepseek.com/v1"
)

真正的“灵魂”在于 Prompt 的设计。我们封装了一个函数,专门用于“问大模型要 SQL”:

def generate_sql_from_nl(query, schema):
    prompt = f"""
    这是一个数据库的 Schema:
    {schema}

    根据这个 Schema, 你能输出一个 SQL 查询来回答以下问题吗?
    只输出 SQL 查询, 不要输出任何其他内容, 也不要带任何格式。

    问题:{query}
    """

    response = client.chat.completions.create(
        model="deepseek-reasoner",
        max_tokens=2048,
        messages=[{
            "role": "user",
            "content": prompt
        }]
    )
    return response.choices[0].message

这里有三条非常重要的 Prompt 约束:

  • 给足上下文:把 Schema 原样给到模型
  • 明确任务:根据 Schema 生成 SQL 来回答问题
  • 强约束输出格式:只允许输出 SQL,不要自然语言解释、不要代码块标记

模型选择的是一个带“推理能力”的版本,这对于理解结构、推导正确字段名非常关键。

五、跑通闭环:从中文问题到 SQL,再到查询结果

现在,我们可以试着用一句自然语言提问,例如:

question = "开发部员工的姓名和工资是多少?"

message = generate_sql_from_nl(question, schema_str)
print("模型生成的 SQL:")
print(message.content)

典型的输出可能是:

SELECT name, salary FROM EMPLOYEES WHERE department = '开发部';

到这里,我们已经成功把“自然语言问题”翻译成了“SQL 查询”。
若再加上执行这条 SQL 的代码,例如:

generated_sql = message.content.strip()

cursor.execute(generated_sql)
rows = cursor.fetchall()
for row in rows:
    print(row)

就可以完整实现:

自然语言提问 → 大模型生成 SQL → 数据库执行 → 输出结果。

这就是最小可用的 Text-to-SQL 系统。

六、为什么要用“推理型”大模型?

在实践中会遇到一些有趣的问题,比如:

  • 字段名拼写问题(department 不小心写成了别的)
  • 表名、字段名大小写不统一
  • 问题本身比较模糊,需要推理才能确定查询逻辑

普通的“补全文本”模型,往往只是根据语料习惯“猜”出一段看起来像 SQL 的东西;
而带推理能力的模型,会:

  • 先认真阅读 Schema
  • 再分析问题意图
  • 最后按结构拼装出一条更合理、更稳健的 SQL

很多 API 还会在内部保留一段“推理内容”,帮助你理解模型是怎么想的,这对于调试 Prompt 和改进系统行为都非常有价值。

七、从示例到实战:可以继续深入的方向

在这个小示例中,我们完成了一条从自然语言到 SQL 的完整链路。接下来可以从几个方向继续扩展:

  • 扩展数据结构

    • 新增更多表(部门表、项目表等)
    • 测试多表关联、聚合查询、排序分页等复杂场景
  • 增强 Prompt 工程

    • 强制只生成 SELECT,防止误生成删除/修改类语句
    • 要求模型在内部“先思考再给答案”,提高准确率
    • 对中文问题引导出更结构化的中间解释
  • 系统化封装

    • 把这套逻辑封装成一个 API 或 Web 服务
    • 前端只需要发中文问题,就能拿到查询结果
    • 后端在执行 SQL 前做白名单/黑名单校验,保证安全

结语

这个小例子展示了在 AIGC 时代,数据库交互可以如何“进化”:

  • 底层依然是经典的关系型数据库和 SQL
  • 上层通过大模型,把“写 SQL”这件事交给了机器
  • 人更多地在思考“我要问什么”而不是“SQL 怎么写”

当你习惯了这种交互方式,就会发现:
数据库不再只是一个冰冷的存储系统,而更像是一个可以对话的“数据助理”。