Text2SQL 实战:自然语言直连数据库,让 AI 自动写 SQL 完成 CRUD 与多表联查
不懂 SQL 也能查数据库?"工程部门员工的姓名和工资是多少?"------一句人话,AI 自动生成
SELECT name, salary FROM EMPLOYEES WHERE department = '工程'。这就是 Text2SQL:自然语言 → SQL 的自动翻译。本文从零搭建一个完整的 Text2SQL 项目:SQLite 文件数据库建表 → Schema 工程化提取 → DeepSeek 生成 SQL → CRUD 四种操作实战 → 多表 JOIN + GROUP BY 聚合查询。核心只有一个函数ask_deepseek,却能覆盖增删改查的全部场景。建议收藏后动手实操。
一、Text2SQL 是什么?
1.1 从"会写代码"到"会说话"
sql
Text2SQL = 自然语言 → SQL 语句
传统方式:
→ 想查数据,必须先会 SQL
→ 想改数据,必须手写 INSERT / UPDATE
→ 数据库操作有门槛
Text2SQL 方式:
→ 用自然语言描述需求
→ AI 自动生成 SQL
→ 直接执行
举例:
┌──────────────────────────────────────────────────────────┐
│ 自然语言 AI 生成的 SQL │
│ │
│ "工程部门员工的姓名和工资" → SELECT name, salary │
│ FROM employees │
│ WHERE department='工程' │
│ │
│ "增加一个新员工张三" → INSERT INTO employees │
│ (name, department, │
│ salary) │
│ VALUES ('张三',...); │
│ │
│ "把王二的工资调到 55000" → UPDATE employees │
│ SET salary=55000 │
│ WHERE name='王二' │
│ │
│ "删除市场部的王二" → DELETE FROM employees │
│ WHERE department='市场' │
│ AND name='王二' │
└──────────────────────────────────────────────────────────┘
1.2 Text2SQL 的本质
sql
Text2SQL 的本质 = 两个技术栈的碰撞
① NLP(自然语言处理)
→ 理解用户意图
→ 提取查询条件
→ 解析语义
② 数据库技术
→ 理解表结构(Schema)
→ 生成合法 SQL
→ 执行查询
LLM 时代的 Text2SQL:
→ 不需要复杂的 NLP 模型
→ 不需要规则引擎
→ 一个 LLM + 一段好 prompt 就能搞定
核心公式:
Text2SQL = Schema + Prompt + LLM
┌──────────────────────────────────────────────────────────┐
│ Text2SQL 的平权意义 │
│ │
│ vibe coding 平权了代码开发 │
│ → 不会编程也能让 AI 写代码 │
│ │
│ text2sql 平权了数据库操作 │
│ → 不会 SQL 也能查数据库 │
│ → 用自然语言描述需求即可 │
│ │
│ 这就是"数据库平权" │
│ → 业务人员直接问数据 │
│ → 不需要经过开发人员转述 │
└──────────────────────────────────────────────────────────┘
二、SQLite:零配置的文件数据库
2.1 为什么选 SQLite?
arduino
SQLite 是什么?
→ 系统内置的文件型关系型数据库
→ 一个 .db 文件就是整个数据库
→ 轻量级数据库引擎
→ 无需单独安装
→ 无需配置
→ 无需管理
┌──────────────────────────────────────────────────────────┐
│ SQLite vs MySQL 对比 │
│ │
│ SQLite MySQL │
│ ───────────────────────────────────────────── │
│ 类型 文件型(.db 文件) 服务器型 │
│ 安装 无需安装 需要安装配置 │
│ 连接 本地文件 网络连接(host/port) │
│ 启动 无需启动 需要启动服务 │
│ 场景 单机/嵌入式/原型 生产环境/并发/多用户 │
│ 并发 弱(写锁) 强(行级锁) │
│ 适合 Python 项目内置 Web 应用 │
│ │
│ Python 内置 sqlite3 模块 │
│ → 不需要 pip install │
│ → import sqlite3 直接可用 │
│ → 原型验证 Text2SQL 的最佳选择 │
└──────────────────────────────────────────────────────────┘
2.2 连接数据库与创建表
python
# 文件型的关系型数据库
import sqlite3
# 连接数据库(不存在会自动创建 test.db 文件)
conn = sqlite3.connect('test.db')
# 创建一个游标对象
# 通过 光标(cursor) 执行 SQL 操作
cursor = conn.cursor()
# 创建 employees 表
cursor.execute("""
CREATE TABLE IF NOT EXISTS employees (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT,
department TEXT,
salary INTEGER
)
""")
# 批量插入示例数据
sample_data = [
(6, '张三', '销售', 5000),
(7, '李四', '市场', 6000),
(8, '王五', '研发', 7000),
(9, '赵六', '工程', 8000),
(10, '王二', '市场', 9000),
]
cursor.executemany(
'INSERT INTO employees VALUES (?,?,?,?)',
sample_data
)
conn.commit()
sql
代码解析:
① sqlite3.connect('test.db')
→ 连接数据库
→ 文件不存在会自动创建
→ 这就是"文件型数据库"的含义
② cursor = conn.cursor()
→ 创建游标对象
→ 游标(cursor)是执行 SQL 的操作手柄
→ 所有 SQL 都通过 cursor 执行
③ cursor.execute(sql)
→ 执行单条 SQL
→ CREATE TABLE 建表
④ cursor.executemany(sql, data)
→ 批量执行 SQL
→ data 是元组列表
→ 每个元组对应一行数据
→ ? 是占位符(参数化,防注入)
⑤ conn.commit()
→ 提交事务
→ 不 commit 数据不会真正写入
→ SQLite 默认手动提交
INSERT INTO employees VALUES (?,?,?,?)
→ ? 占位符
→ executemany 自动绑定每个元组
→ 防止 SQL 注入
2.3 事务 Transaction
sql
readme 中的事务概念:
insertmany → INSERT INTO XXX VALUES (?, ?, ?, ?)
事务(Transaction):
→ 一组操作的集合
→ 要么全部成功,要么全部失败
→ 保证数据一致性
经典案例(下单流程):
┌──────────────────────────────────────────────────────────┐
│ 下单事务:三个步骤 │
│ │
│ ① 订单表插入订单(order) │
│ → INSERT INTO orders ... │
│ │
│ ② 商品表库存减一(product count-1) │
│ → UPDATE products SET count = count - 1 │
│ │
│ ③ 付款记录(pay) │
│ → INSERT INTO payments ... │
│ │
│ 事务保证: │
│ → 三步全部成功 → commit 生效 │
│ → 任一步失败 → rollback 回滚 │
│ → 不会出现"扣了库存没付款"的情况 │
└──────────────────────────────────────────────────────────┘
Python 中的事务:
conn.commit() # 全部成功,提交
conn.rollback() # 失败回滚
为什么需要事务?
→ 数据库操作可能中途失败
→ 部分成功 = 数据不一致
→ 事务保证"全有或全无"
三、Schema 工程:让 AI 看懂数据库
3.1 为什么需要 Schema?
sql
LLM 不知道你的数据库长什么样
→ LLM 是"通用智能"
→ 它不知道你有 employees 表
→ 不知道有 name / department / salary 字段
→ 不知道 salary 是 INTEGER
要生成正确的 SQL
→ 必须告诉 LLM 表结构
→ 这个"表结构描述"就是 Schema
→ Schema 是 LLM 理解数据库的桥梁
类比:
→ 让新员工干活,先给 TA 看组织架构图
→ 让 AI 写 SQL,先给它看表结构
→ Schema = 数据库的"组织架构图"
3.2 用 PRAGMA 提取 Schema
python
# 获取数据库 Schema
# 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)
sql
输出结果:
PRAGMA table_info(employees) 返回:
[(0, 'id', 'INTEGER', 0, None, 1),
(1, 'name', 'TEXT', 0, None, 0),
(2, 'department', 'TEXT', 0, None, 0),
(3, 'salary', 'INTEGER', 0, None, 0)]
每个元组的含义:
(cid, name, type, notnull, dflt_value, pk)
→ 0: 列序号
→ 'id': 列名
→ 'INTEGER': 数据类型
→ 0: 是否非空
→ None: 默认值
→ 1: 是否主键
转换后的 Schema 字符串:
CREATE TABLE EMPLOYEES (
id INTEGER
name TEXT
department TEXT
salary INTEGER
)
关键代码:
col[1] → 列名(第 1 个元素)
col[2] → 类型(第 2 个元素)
f'{col[1]} {col[2]}' → "id INTEGER"
'\n'.join([...]) → 每列一行拼接
3.3 Schema 工程的核心思想
sql
Schema 为什么是"工程"?
┌──────────────────────────────────────────────────────────┐
│ Schema 的作用: │
│ │
│ ① 表名和列名准确 │
│ → AI 知道表叫 employees 而不是 staff │
│ → AI 知道列叫 department 而不是 dept │
│ │
│ ② 类型信息完整 │
│ → salary 是 INTEGER(数字) │
│ → name 是 TEXT(字符串) │
│ → AI 知道该用什么格式的值 │
│ │
│ ③ 表间关系可推断 │
│ → employees.department = departments.name │
│ → AI 能自动发现关联字段 │
│ → 才能生成 JOIN 语句 │
│ │
│ Schema 质量决定 SQL 质量 │
│ → Schema 越详细,SQL 越准确 │
│ → Schema 缺失,AI 只能瞎猜 │
│ → 这就是"Schema 工程"的重要性 │
└──────────────────────────────────────────────────────────┘
四、ask_deepseek:Text2SQL 核心函数
4.1 环境配置
python
import os
from openai import OpenAI
# 从环境变量读取(不要把密钥写死在代码里!)
client = OpenAI(
api_key=os.getenv('OPENAI_API_KEY'),
base_url='https://api.deepseek.com/v1'
)
ini
安全提示:
❌ 错误做法(密钥硬编码):
client = OpenAI(api_key='sk-xxxxx')
✅ 正确做法(环境变量):
client = OpenAI(api_key=os.getenv('OPENAI_API_KEY'))
→ 密钥写进代码 = 密钥泄露
→ 一旦提交到 GitHub 就废了
→ 用环境变量 / .env 文件管理
→ 生产环境用密钥管理服务
4.2 核心函数实现
python
# text2sql 数据库平权
# vibe coding 平权了代码开发
def ask_deepseek(query, schema):
# 模板:Schema + 问题 → 生成 SQL
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
sql
Prompt 模板设计解析:
┌──────────────────────────────────────────────────────────┐
│ Prompt 四要素 │
│ │
│ ① 提供 Schema │
│ "这是一个数据库的Schema:{schema}" │
│ → AI 知道表结构 │
│ → 这是生成正确 SQL 的前提 │
│ │
│ ② 明确任务 │
│ "请输出一个SQL查询来回答以下问题" │
│ → 告诉 AI 要做什么 │
│ → 目标明确:生成 SQL │
│ │
│ ③ 输出约束(关键!) │
│ "只输出SQL 查询语句本身" │
│ "不要使用任何Markdown格式" │
│ "不要包含反引号、代码块标记或额外说明" │
│ → 防止 AI 返回 ```sql 代码块 │
│ → 防止 AI 添加解释文字 │
│ → 让结果可以直接执行 │
│ │
│ ④ 问题输入 │
│ "问题:{query}" │
│ → 用户的具体查询需求 │
│ │
│ 为什么"只输出 SQL"这么重要? │
│ → 如果 AI 返回 ```sql\nSELECT...\n``` │
│ → cursor.execute 会报错(反引号不是合法 SQL) │
│ → 必须让输出"纯净" │
│ → 直接可执行 │
└──────────────────────────────────────────────────────────┘
4.3 调用模型
ini
response 的取值路径:
response
└── choices # 候选结果列表(通常一个)
└── [0] # 第一个结果
└── message
└── content # AI 生成的 SQL 文本
response.choices[0].message.content
→ 就是生成的 SQL 语句字符串
model='deepseek-v4-flash'
→ DeepSeek 的快速模型
→ 生成 SQL 这类简单任务,快模型足够
→ 速度快、成本低
max_tokens=2048
→ 限制生成的最大 token 数
→ SQL 通常很短,2048 足够
→ 防止 AI 无限生成
五、CRUD 全家桶实战
5.1 SELECT:查询
python
question = '工程部门员工的姓名和工资是多少?'
sql_query = ask_deepseek(question, schema_str)
print(sql_query)
# SELECT name, salary FROM EMPLOYEES WHERE department = '工程';
results = cursor.execute(sql_query).fetchall()
for row in results:
print(row)
# ('赵六', 8000)
sql
执行流程:
问题:"工程部门员工的姓名和工资是多少?"
│
│ ask_deepseek(question, schema_str)
│ → LLM 解析意图:
│ "工程部门" → WHERE department = '工程'
│ "姓名和工资" → SELECT name, salary
│
▼
生成的 SQL:
SELECT name, salary FROM EMPLOYEES WHERE department = '工程';
│
│ cursor.execute(sql_query).fetchall()
│
▼
执行结果:
('赵六', 8000)
AI 的正确理解:
→ "工程部门" → department = '工程'(字符串要加引号)
→ "姓名和工资" → 只查这两个字段(不查 *)
→ 字段顺序:name 在前,salary 在后
5.2 INSERT:插入
python
question = '在销售部门增加一个新员工,姓名为张三,工资为45000'
sql_query = ask_deepseek(question, schema_str)
print(sql_query)
# INSERT INTO EMPLOYEES (name, department, salary) VALUES ('张三', '销售', 45000);
cursor.execute(sql_query)
conn.commit()
sql
INSERT 生成的 SQL 分析:
问题:"在销售部门增加一个新员工,姓名为张三,工资为45000"
INSERT INTO EMPLOYEES (name, department, salary)
VALUES ('张三', '销售', 45000);
AI 的正确理解:
→ "增加一个新员工" → INSERT INTO
→ "姓名为张三" → name = '张三'
→ "销售部门" → department = '销售'
→ "工资为45000" → salary = 45000
→ 字段名明确列出(id 自增,不需要指定)
→ 字符串加引号,数字不加
注意:
→ 生成的 SQL 指定了字段列表 (name, department, salary)
→ 没有指定 id(AUTOINCREMENT 自动生成)
→ 这是"正确"的 INSERT 写法
5.3 UPDATE:更新
python
question = '将王二的工资调整为55000'
sql_query = ask_deepseek(question, schema_str)
print(sql_query)
# UPDATE EMPLOYEES SET salary = 55000 WHERE name = '王二';
cursor.execute(sql_query)
conn.commit()
sql
UPDATE 生成的 SQL 分析:
问题:"将王二的工资调整为55000"
UPDATE EMPLOYEES SET salary = 55000 WHERE name = '王二';
AI 的正确理解:
→ "将...调整" → UPDATE
→ "工资调整为55000" → SET salary = 55000
→ "王二" → WHERE name = '王二'
关键点:
→ WHERE 条件必须带上!
→ 否则会更新所有行(灾难)
→ AI 能正确识别更新条件
5.4 DELETE:删除
python
question = '删除部门为市场的王二'
sql_query = ask_deepseek(question, schema_str)
print(sql_query)
# DELETE FROM EMPLOYEES WHERE department = '市场' AND name = '王二'
cursor.execute(sql_query)
conn.commit()
sql
DELETE 生成的 SQL 分析:
问题:"删除部门为市场的王二"
DELETE FROM EMPLOYEES WHERE department = '市场' AND name = '王二'
AI 的正确理解:
→ "删除" → DELETE FROM
→ "部门为市场的" → department = '市场'
→ "王二" → name = '王二'
→ 两个条件用 AND 连接
关键点:
→ 条件越精确越安全
→ AI 同时用了两个条件
→ 避免误删同名员工
→ 这是"好的 DELETE"
5.5 验证:查询全部
python
question = '查询所有员工的信息'
sql_query = ask_deepseek(question, schema_str)
print(sql_query)
# SELECT * FROM EMPLOYEES;
results = cursor.execute(sql_query).fetchall()
for row in results:
print(row)
# (6, '张三', '销售', 5000)
# (7, '李四', '市场', 6000)
# (8, '王五', '研发', 7000)
# (9, '赵六', '工程', 8000)
# (11, '张三', '销售', 45000) ← 新增的张三
sql
CRUD 全家桶总结:
┌──────────────────────────────────────────────────────────┐
│ CRUD 四种操作 │
│ │
│ 操作 自然语言 生成的 SQL │
│ ───────────────────────────────────────────────── │
│ Create "增加新员工张三" INSERT INTO ... │
│ Read "工程部门员工" SELECT ... WHERE ... │
│ Update "工资调为55000" UPDATE ... SET ... │
│ Delete "删除市场王二" DELETE ... WHERE ... │
│ │
│ 一个 ask_deepseek 函数搞定四种操作 │
│ → 不需要为每种操作写不同代码 │
│ → LLM 自动判断是增删改查中的哪一种 │
│ → 这就是 Text2SQL 的威力 │
└──────────────────────────────────────────────────────────┘
六、多表联查进阶
6.1 创建第二张表
python
cursor.execute("""
CREATE TABLE IF NOT EXISTS departments(
id INTEGER PRIMARY KEY,
name TEXT,
manager TEXT
)
""")
sample_departments = [
(1, '销售', '王经理'),
(2, '工程', '李经理'),
(3, '市场', '张经理')
]
cursor.executemany(
'INSERT INTO departments VALUES (?,?,?)',
sample_departments
)
conn.commit()
bash
两张表的关系:
employees 表 departments 表
┌────┬──────┬──────────┬───────┐ ┌────┬──────┬───────┐
│ id │ name │department│salary │ │ id │ name │manager│
├────┼──────┼──────────┼───────┤ ├────┼──────┼───────┤
│ 6 │ 张三 │ 销售 │ 5000 │ │ 1 │ 销售 │ 王经理 │
│ 7 │ 李四 │ 市场 │ 6000 │ │ 2 │ 工程 │ 李经理 │
│ 8 │ 王五 │ 研发 │ 7000 │ │ 3 │ 市场 │ 张经理 │
│ 9 │ 赵六 │ 工程 │ 8000 │ └────┴──────┴───────┘
│ 11 │ 张三 │ 销售 │ 45000 │
└────┴──────┴──────────┴───────┘
关联字段:
→ employees.department = departments.name
→ "销售" / "工程" / "市场" 是连接点
→ 通过这个关系做 JOIN
6.2 完整 Schema 生成(多表)
python
# 获取完整的数据库 schema
tables = ['employees', 'departments']
schema_str = ''
for table in tables:
schema = cursor.execute(f'PRAGMA table_info({table})').fetchall()
schema_str += f'CREATE TABLE {table} (\n' + \
'\n'.join([f'{col[1]} {col[2]}' for col in schema]) + \
'\n);\n\n'
print('完整的数据库schema:')
print(schema_str)
sql
多表 Schema 生成:
循环遍历所有表:
→ employees → 生成 CREATE TABLE employees (...)
→ departments → 生成 CREATE TABLE departments (...)
输出:
CREATE TABLE employees (
id INTEGER
name TEXT
department TEXT
salary INTEGER
);
CREATE TABLE departments (
id INTEGER
name TEXT
manager TEXT
);
价值:
→ AI 同时看到两张表的结构
→ 才能推断表间关系
→ 才能生成 JOIN 语句
→ 单表 Schema 只能做单表查询
6.3 多表 JOIN + 聚合查询
python
question = '根据两个表之间的关系,列出每个部门的员工人数和平均工资'
sql_query = ask_deepseek(question, schema_str)
print(sql_query)
# SELECT d.name AS department, COUNT(e.id) AS employee_count,
# AVG(e.salary) AS average_salary
# FROM departments d
# LEFT JOIN employees e ON d.name = e.department
# GROUP BY d.name;
results = cursor.execute(sql_query).fetchall()
for row in results:
print(row)
# ('工程', 1, 8000.0)
# ('市场', 1, 6000.0)
# ('销售', 2, 25000.0)
vbnet
这条 SQL 是全篇最高级的查询:
SELECT d.name AS department,
COUNT(e.id) AS employee_count,
AVG(e.salary) AS average_salary
FROM departments d
LEFT JOIN employees e ON d.name = e.department
GROUP BY d.name;
逐段解析:
① SELECT d.name AS department
→ 查部门名称
→ d 是 departments 的别名
→ AS 给列起别名(显示为 department)
② COUNT(e.id) AS employee_count
→ 统计每个部门的员工人数
→ COUNT 聚合函数
→ 统计 e.id 非空的记录数
③ AVG(e.salary) AS average_salary
→ 计算平均工资
→ AVG 聚合函数
→ 平均值
④ FROM departments d
→ 以 departments 为主表
→ 左连接(LEFT JOIN)
→ 保证所有部门都显示(即使没有员工)
⑤ LEFT JOIN employees e ON d.name = e.department
→ 关联条件:部门名相等
→ 员工表匹配到对应部门
⑥ GROUP BY d.name
→ 按部门分组
→ COUNT 和 AVG 按组计算
执行结果:
('工程', 1, 8000.0) → 工程部 1 人,平均 8000
('市场', 1, 6000.0) → 市场部 1 人,平均 6000
('销售', 2, 25000.0) → 销售部 2 人,平均 25000
→ 销售部平均 25000 = (5000 + 45000) / 2
→ 数据完全正确!
sql
JOIN 方向的选择:
需求:"列出每个部门的员工人数和平均工资"
→ 主语是"部门"
→ 所有部门都要显示
→ 即使某部门没有员工也要出现(显示 0)
用 LEFT JOIN(左连接):
→ departments 在左(主表)
→ employees 在右(从表)
→ 左表所有行都保留
→ 右表没有匹配 → 填 NULL
┌──────────────────────────────────────────────────────┐
│ LEFT JOIN: │
│ 部门(左) 员工(右) │
│ 销售 ←────────────── 张三、张三(45000) │
│ 工程 ←────────────── 赵六 │
│ 市场 ←────────────── 李四 │
│ 研发 ←────────────── (员工表没有研发?) │
│ → 研发也要显示,员工为 0 │
│ → 这就是 LEFT JOIN 的意义 │
│ │
│ RIGHT JOIN: │
│ → 以右表(员工)为主 │
│ → 员工表所有行都保留 │
│ → 没有部门的员工也会显示 │
│ → 与需求"以部门为主"相反 │
└──────────────────────────────────────────────────────┘
七、Text2SQL 的工程化与安全
7.1 风险:AI 生成的 SQL 能直接执行吗?
sql
Text2SQL 的最大风险 = 写操作
AI 生成的 SELECT:
→ 只读操作,风险低
→ 查错数据顶多结果不对
AI 生成的 INSERT / UPDATE / DELETE:
→ 写操作,风险高!
→ 一条错误的 DELETE 可能删光全表
→ 必须人工审查!
┌──────────────────────────────────────────────────────────┐
│ 安全策略: │
│ │
│ ① 区分读写 │
│ → SELECT 可以自动执行 │
│ → INSERT / UPDATE / DELETE 必须人工确认 │
│ │
│ ② 只读权限 │
│ → 查询场景用只读数据库账号 │
│ → 即使 SQL 错了也删不了数据 │
│ │
│ ③ 事务包裹 │
│ → 写操作放事务里 │
│ → 执行前备份,出错回滚 │
│ │
│ ④ SQL 白名单 / 校验 │
│ → 检查是否包含 DROP / TRUNCATE 等危险语句 │
│ → 阻止危险操作 │
│ │
│ ⑤ 人机协同 │
│ → AI 生成 SQL → 人确认 → 执行 │
│ → 这就是"人在回路"(Human in the Loop) │
└──────────────────────────────────────────────────────────┘
7.2 Prompt 工程要点回顾
sql
Text2SQL 的 Prompt 设计要点:
① Schema 完整
→ 所有表、所有字段、所有类型
→ 这是生成正确 SQL 的基石
② 输出纯净
→ "只输出 SQL 语句本身"
→ "不要 Markdown 格式"
→ "不要反引号"
→ "不要额外说明"
→ 保证结果可直接执行
③ 任务明确
→ "请输出一个 SQL 查询来回答以下问题"
→ 不拐弯抹角
④ 模型选型
→ 简单查询:flash 类快模型(快、便宜)
→ 复杂查询:更强大的模型(准确率高)
7.3 Text2SQL 的应用场景
sql
Text2SQL 的落地场景:
① 数据分析平权
→ 业务人员直接问数据
→ "上个月销售额是多少?"
→ 不需要懂 SQL
② 智能客服
→ 用户查订单状态
→ 自动转 SQL 查询
→ 返回结果给用户
③ 自然语言报表
→ "按部门统计平均工资"
→ 自动生成聚合查询
→ 自动出报表
④ Agent + 数据库
→ AI Agent 作为 DBA
→ 自主查询、自主分析
→ 这是 RAG 之外的另一条数据通路
八、总结
8.1 知识体系图
sql
Text2SQL 完整实战
│
├── Text2SQL 是什么
│ ├── 自然语言 → SQL 语句
│ ├── 核心公式:Schema + Prompt + LLM
│ └── 平权意义:不会 SQL 也能查数据库
│
├── SQLite 文件数据库
│ ├── 文件型(.db),无需安装配置
│ ├── sqlite3.connect('test.db')
│ ├── cursor 游标执行 SQL
│ ├── executemany 批量插入
│ ├── conn.commit() 提交事务
│ └── 事务:订单/商品/付款 全有或全无
│
├── Schema 工程
│ ├── PRAGMA table_info(表名) 提取结构
│ ├── (cid, name, type, notnull, dflt_value, pk)
│ ├── 列表推导式拼接 schema 字符串
│ ├── 多表循环生成完整 schema
│ └── Schema 是 LLM 理解数据库的桥梁
│
├── ask_deepseek 核心函数
│ ├── OpenAI 客户端 + DeepSeek API
│ ├── Prompt 四要素
│ │ ├── Schema 提供表结构
│ │ ├── 明确生成 SQL 的任务
│ │ ├── 输出约束(只输出 SQL,不要 Markdown)
│ │ └── 用户问题输入
│ └── response.choices[0].message.content
│
├── CRUD 全家桶
│ ├── SELECT:查询(WHERE 条件)
│ ├── INSERT:插入(字段列表 + VALUES)
│ ├── UPDATE:更新(SET + WHERE)
│ ├── DELETE:删除(精确 WHERE 条件)
│ └── 一个函数搞定四种操作
│
├── 多表联查进阶
│ ├── 第二张表 departments
│ ├── 关联字段:employees.department = departments.name
│ ├── LEFT JOIN 主表保留所有行
│ ├── GROUP BY 分组
│ ├── COUNT 计数 + AVG 平均
│ └── AS 别名
│
├── 工程化与安全
│ ├── 密钥用环境变量,不硬编码
│ ├── 写操作必须人工审查
│ ├── 只读账号 / 事务包裹 / SQL 校验
│ └── 人机协同(Human in the Loop)
│
└── 应用场景
├── 数据分析平权
├── 智能客服
├── 自然语言报表
└── Agent + 数据库(AI 当 DBA)
8.2 一句话总结
Text2SQL 的核心公式是 Schema + Prompt + LLM:先用
PRAGMA table_info提取表结构拼成 Schema 字符串,再通过精心设计的 Prompt(提供 Schema → 明确任务 → 约束输出格式"只输出 SQL 不要 Markdown")让 LLM 生成纯净可执行的 SQL,最后用cursor.execute直接执行。一个ask_deepseek函数就能覆盖增删改查全部操作:"工程部门员工的姓名和工资"生成 SELECT,"增加一个新员工"生成 INSERT,"工资调整为 55000"生成 UPDATE,"删除市场部的王二"生成 DELETE;多表场景下 AI 甚至能自动生成 LEFT JOIN + GROUP BY + COUNT/AVG 的聚合查询。SQLite 作为零配置的文件数据库,是验证 Text2SQL 的最佳载体。但要记住:SELECT 可以自动执行,INSERT / UPDATE / DELETE 必须人工审查------AI 生成 SQL 的能力再强,写操作的安全底线不能丢。这就是"数据库平权"的正确打开方式:AI 负责写,人负责审。
如果这篇文章对你有帮助,欢迎点赞 和收藏!