「AI Python 系列」第 01 栏 · AI 时代的 Python 办公自动化
全栏 18 篇 · 零成本跟完 🍃
品牌:梅雅达编程笔记
摘要: 不会写SQL也能查数据库。本篇用AI把自然语言转成SQL语句,执行查询后再用AI解读结果。内置示例数据库(员工/产品/销售三张表),运行即生成。三个实战覆盖基础查询、聚合统计、多表关联加AI解读。传统做法手写SQL查完还得自己分析,AI做法提问即得答案。
开篇 · 数据查询的真实痛点
你有没有遇到过这些场景:
| 查销售数据 | 写 SQL → 执行 → 看结果 | 不会 SQL 的同事只能等技术部 |
| 部门统计 | GROUP BY + 聚合函数 | 写了半天还是报错 |
| 多表关联 | JOIN 写到怀疑人生 | 三张表关联,SQL 写了20行 |
| 结果解读 | 看到一堆数字不知道啥意思 | 还要自己算环比、做对比 |
核心矛盾: 数据在数据库里,但用数据的人不会写 SQL。
今天用 AI 解决这个矛盾——你说人话,AI 帮你翻译成 SQL,查完还帮你解释。
一、传统做法 vs AI 时代做法
传统做法:手写 SQL
import sqlite3
conn = sqlite3.connect("company.db")
c = conn.cursor()
# 手写 SQL 查询
c.execute("""
SELECT p.name, p.category, SUM(s.quantity) as total_qty, SUM(s.amount) as total_amount
FROM sales s
JOIN products p ON s.product_id = p.id
GROUP BY p.category
ORDER BY total_amount DESC
""")
results = c.fetchall()
print(results)
# 然后你自己看数字,自己分析…
conn.close()
能力边界:
- ✅ 能查任何数据
- ❌ 需要记 SQL 语法
- ❌ 需要了解表结构
- ❌ 查出来是一堆数字,还要自己解读
AI 时代做法:自然语言 → SQL → 解读
# 你只管说人话
answer = ask("哪个产品类别销售额最高?")
# AI 自动完成:
# 1. 理解问题 → 生成 SQL
# 2. 执行 SQL → 获取结果
# 3. 解读结果 → 给你中文结论
组合优势:
-
AI 负责翻译:自然语言 → SQL,结果 → 中文解读
sqlite3 负责执行:跑查询、拿数据
-
你只需要提问
二、数据库结构
本篇用 Python 内置的 sqlite3 自动创建示例数据库,运行代码即生成,不用提前准备:
company.db
├── employees(员工表)
│ ├── id, name, department, position, salary, hire_date
│ └── 10 条数据(销售部/技术部/人事部/市场部)
│
├── products(产品表)
│ ├── id, name, category, price, stock
│ └── 8 条数据(教育/软件/工具三个类别)
│
└── sales(销售记录表)
├── id, product_id, employee_id, quantity, sale_date, amount
└── 15 条数据(2024年1月-7月)
三张表通过 product_id 和 employee_id 关联,可以做多表 JOIN 查询。
三、完整代码
"""
nl2sql.py – 自然语言查数据库(NL2SQL)
用 AI 把自然语言转成 SQL,执行查询,再用 AI 解释结果
⚠️ 安全限制:只允许 SELECT 查询,禁止增删改操作
"""
import os
import sys
import json
import sqlite3
# 添加上级目录到路径,引用 llm_client.py
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from llm_client import LLMClient
def create_database(db_path="company.db"):
"""创建示例数据库:员工表、产品表、销售记录表"""
conn = sqlite3.connect(db_path)
c = conn.cursor()
# ———- 员工表 ———-
c.execute("DROP TABLE IF EXISTS employees")
c.execute("""
CREATE TABLE employees (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
department TEXT,
position TEXT,
salary REAL,
hire_date TEXT
)
""")
employees = [
(1, "张伟", "销售部", "销售经理", 15000, "2020-03-15"),
(2, "李娜", "技术部", "后端工程师", 18000, "2019-07-01"),
(3, "王芳", "销售部", "销售专员", 9000, "2022-06-10"),
(4, "刘强", "技术部", "前端工程师", 16000, "2021-03-20"),
(5, "陈静", "人事部", "HR经理", 14000, "2020-11-05"),
(6, "赵磊", "销售部", "销售专员", 9500, "2023-02-14"),
(7, "孙丽", "市场部", "市场专员", 10000, "2022-09-01"),
(8, "周明", "技术部", "技术总监", 28000, "2018-01-15"),
(9, "吴秀", "人事部", "HR专员", 7500, "2023-05-20"),
(10, "郑凯", "市场部", "市场经理", 16000, "2020-07-10"),
]
c.executemany("INSERT INTO employees VALUES (?,?,?,?,?,?)", employees)
# ———- 产品表 ———-
c.execute("DROP TABLE IF EXISTS products")
c.execute("""
CREATE TABLE products (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
category TEXT,
price REAL,
stock INTEGER
)
""")
products = [
(1, "梅雅达编程教程(基础版)", "教育", 99, 500),
(2, "梅雅达编程教程(进阶版)", "教育", 199, 300),
(3, "智能客服系统", "软件", 9999, 50),
(4, "数据可视化大屏", "软件", 15000, 20),
(5, "Python爬虫工具包", "工具", 299, 200),
(6, "自动化办公脚本库", "工具", 499, 150),
(7, "AI提示词工程手册", "教育", 59, 800),
(8, "企业级API网关", "软件", 25000, 10),
]
c.executemany("INSERT INTO products VALUES (?,?,?,?,?)", products)
# ———- 销售记录表 ———-
c.execute("DROP TABLE IF EXISTS sales")
c.execute("""
CREATE TABLE sales (
id INTEGER PRIMARY KEY,
product_id INTEGER,
employee_id INTEGER,
quantity INTEGER,
sale_date TEXT,
amount REAL,
FOREIGN KEY (product_id) REFERENCES products(id),
FOREIGN KEY (employee_id) REFERENCES employees(id)
)
""")
sales = [
(1, 1, 1, 50, "2024-01-15", 4950),
(2, 3, 3, 2, "2024-01-20", 19998),
(3, 7, 6, 100, "2024-02-03", 5900),
(4, 2, 1, 30, "2024-02-14", 5970),
(5, 5, 3, 20, "2024-03-01", 5980),
(6, 1, 6, 80, "2024-03-10", 7920),
(7, 4, 10, 1, "2024-03-22", 15000),
(8, 8, 1, 1, "2024-04-05", 25000),
(9, 6, 6, 15, "2024-04-18", 7485),
(10, 3, 3, 3, "2024-05-02", 29997),
(11, 7, 6, 150, "2024-05-20", 8850),
(12, 2, 1, 40, "2024-06-01", 7960),
(13, 5, 3, 25, "2024-06-15", 7475),
(14, 8, 10, 2, "2024-07-03", 50000),
(15, 1, 6, 60, "2024-07-10", 5940),
]
c.executemany("INSERT INTO sales VALUES (?,?,?,?,?,?)", sales)
conn.commit()
conn.close()
print(f" ✅ 数据库已创建:{db_path}")
print(f" 3张表 · 员工10人 · 产品8个 · 销售记录15条")
def get_schema(db_path="company.db"):
"""获取数据库结构信息,用于 AI 理解表结构"""
conn = sqlite3.connect(db_path)
c = conn.cursor()
c.execute("SELECT name FROM sqlite_master WHERE type='table'")
tables = [row[0] for row in c.fetchall()]
schema_parts = []
for table in tables:
c.execute(f"PRAGMA table_info({table})")
columns = c.fetchall()
col_info = ", ".join([f"{col[1]} {col[2]}" for col in columns])
# 获取前3行示例数据
c.execute(f"SELECT * FROM {table} LIMIT 3")
sample_rows = c.fetchall()
sample_str = "\\n".join([str(row) for row in sample_rows])
schema_parts.append(
f"表名: {table}\\n字段: {col_info}\\n示例数据:\\n{sample_str}"
)
conn.close()
return "\\n\\n".join(schema_parts)
def nl2sql(question, schema):
"""AI 将自然语言转成 SQL"""
client = LLMClient(provider="glm")
system_prompt = """你是一个 SQL 专家。根据用户的自然语言问题和数据库结构,生成对应的 SQL 查询语句。
规则:
1. 只输出 SQL 语句,不要解释,不要 markdown 标记
2. 使用标准 SQLite 语法
3. 只允许 SELECT 查询,禁止 INSERT/UPDATE/DELETE/DROP
4. 如果问题不明确,做合理假设并生成最接近的查询
5. 表名和字段名用原始名称,不要加引号"""
prompt = f"""数据库结构:
{schema}
用户问题:{question}
请生成 SQL 查询语句(只输出 SQL,不要其他内容):"""
sql = client.chat(
prompt, system_prompt=system_prompt,
temperature=0.1, max_tokens=500
)
# 清理 AI 可能添加的 markdown 标记
sql = sql.strip()
if sql.startswith("```"):
sql = sql.split("\\n", 1)[1] if "\\n" in sql else sql[3:]
if sql.endswith("```"):
sql = sql[:–3]
sql = sql.strip()
return sql
def execute_sql(db_path, sql):
"""执行 SQL 并返回结果(安全限制:只允许 SELECT)"""
sql_upper = sql.upper().strip()
forbidden = ["INSERT", "UPDATE", "DELETE", "DROP", "ALTER",
"CREATE", "TRUNCATE"]
for kw in forbidden:
if kw in sql_upper:
raise ValueError(f"安全限制:禁止 {kw} 操作,只允许 SELECT 查询")
conn = sqlite3.connect(db_path)
c = conn.cursor()
c.execute(sql)
results = c.fetchall()
columns = [desc[0] for desc in c.description] if c.description else []
conn.close()
return columns, results
def explain_results(question, sql, columns, results):
"""AI 解释查询结果"""
client = LLMClient(provider="glm")
# 限制结果长度,避免 token 过多
display_results = results[:50]
results_str = "\\n".join([str(row) for row in display_results])
prompt = f"""用户问题:{question}
执行的 SQL:{sql}
查询结果(共{len(results)}行,显示前{len(display_results)}行):
列名:{columns}
{results_str}
请用简洁的中文回答用户的问题,基于查询结果给出明确结论。
如果有数字,用表格或列表清晰呈现。不要重复原始数据,要给出分析和结论。"""
return client.chat(prompt, temperature=0.3, max_tokens=800)
def ask(question, db_path="company.db"):
"""完整流程:自然语言 → SQL → 执行 → 解释"""
print(f"\\n ❓ 问题:{question}")
# 1. 获取数据库结构
schema = get_schema(db_path)
# 2. AI 生成 SQL
sql = nl2sql(question, schema)
print(f" 📝 SQL:{sql}")
# 3. 执行 SQL
columns, results = execute_sql(db_path, sql)
print(f" 📊 结果:{len(results)} 行")
for row in results[:5]:
print(f" {row}")
if len(results) > 5:
print(f" …(共 {len(results)} 行)")
# 4. AI 解释
explanation = explain_results(question, sql, columns, results)
print(f"\\n 💡 AI 解答:")
for line in explanation.split("\\n"):
print(f" {line}")
return explanation
def main():
print("=" * 60)
print(" 自然语言查数据库(NL2SQL)")
print("=" * 60)
# 1. 创建数据库
base_dir = os.path.dirname(os.path.abspath(__file__))
db_path = os.path.join(base_dir, "company.db")
create_database(db_path)
# 实战1:基础查询
print("\\n" + "=" * 60)
print(" 实战1:基础查询")
print("=" * 60)
ask("销售额最高的5笔交易是哪些?", db_path)
# 实战2:聚合统计
print("\\n" + "=" * 60)
print(" 实战2:聚合统计")
print("=" * 60)
ask("每个部门的平均工资是多少?按平均工资从高到低排列", db_path)
# 实战3:多表关联 + AI 解读
print("\\n" + "=" * 60)
print(" 实战3:多表关联 + AI 解读")
print("=" * 60)
ask("哪个产品类别销售额最高?列出该类别下所有产品的销量和金额", db_path)
print("\\n" + "=" * 60)
print(" 全部完成!")
print("=" * 60)
print(f"\\n 💾 数据库文件:{db_path}")
print(f" 💡 可用 'sqlite3 {db_path}' 打开查看")
if __name__ == "__main__":
main()
运行方式
# 安装依赖(sqlite3 是内置库,只需 openai 和 dotenv)
cd day11_code
pip install -r requirements.txt
# 运行
python nl2sql.py
预期输出(截选)
============================================================
自然语言查数据库(NL2SQL)
============================================================
✅ 数据库已创建:company.db
3张表 · 员工10人 · 产品8个 · 销售记录15条
============================================================
实战1:基础查询
============================================================
❓ 问题:销售额最高的5笔交易是哪些?
📝 SQL:SELECT s.id, p.name, s.quantity, s.sale_date, s.amount
FROM sales s JOIN products p ON s.product_id = p.id
ORDER BY s.amount DESC LIMIT 5
📊 结果:5 行
(14, '企业级API网关', 2, '2024-07-03', 50000.0)
(10, '智能客服系统', 3, '2024-05-02', 29997.0)
…
💡 AI 解答:
销售额最高的5笔交易如下:
1. 企业级API网关 – 2套 – ¥50,000(2024-07-03)
2. 智能客服系统 – 3套 – ¥29,997(2024-05-02)
…
单笔最高的是API网关,软件类产品客单价最高。
============================================================
实战3:多表关联 + AI 解读
============================================================
❓ 问题:哪个产品类别销售额最高?列出该类别下所有产品的销量和金额
📝 SQL:SELECT p.category, p.name, SUM(s.quantity) as total_qty,
SUM(s.amount) as total_amount
FROM sales s JOIN products p ON s.product_id = p.id
GROUP BY p.category, p.name ORDER BY total_amount DESC
📊 结果:7 行
…
💡 AI 解答:
销售额最高的类别是软件类,总销售额 ¥104,995。
其中API网关贡献最大(¥75,000),其次是智能客服系统(¥49,995)…
四、代码拆解:四步流水线
整个 ask() 函数是一条四步流水线:
| ① 获取表结构 | get_schema() | — | 手动看文档 |
| ② 自然语言→SQL | nl2sql() | 翻译官 | 手写 SQL |
| ③ 执行 SQL | execute_sql() | — | 手动执行 |
| ④ 结果→中文解读 | explain_results() | 分析师 | 自己看数字 |
关键设计:安全第一
def execute_sql(db_path, sql):
"""安全限制:只允许 SELECT"""
forbidden = ["INSERT", "UPDATE", "DELETE", "DROP", "ALTER",
"CREATE", "TRUNCATE"]
for kw in forbidden:
if kw in sql.upper():
raise ValueError(f"安全限制:禁止 {kw} 操作")
AI 生成的 SQL 在执行前会做关键词检查,任何增删改操作都会被拦截。这是防止 AI 误操作的底线。
关键设计:给 AI 看表结构
def get_schema(db_path):
# 返回格式:
# 表名: employees
# 字段: id INTEGER, name TEXT, department TEXT, …
# 示例数据: (1, '张伟', '销售部', '销售经理', 15000.0, '2020-03-15')
AI 生成 SQL 需要知道表名、字段名和数据类型。我们用 PRAGMA table_info 自动获取,再加 3 行示例数据帮 AI 理解数据含义。
📊 本篇成本透明栏
| API 调用次数 | 9 次(3个问题 × 3步:生成SQL + 生成SQL + 解释结果) |
| 消耗 Token | 约 12,000 tokens |
| 成本 | ¥0(GLM-4.7-Flash 永久免费) |
| 免费额度是否够 | ✅ 够用 |
| 商用估算 | ¥0(GLM)/ 约¥0.02(DeepSeek)/ 约¥0.04(Qwen) |
| 接入真实数据库预估 | MySQL/PostgreSQL 换掉 sqlite3 即可 |
练手改造题
改造 1(基础): 给 ask() 函数加一个"追问"功能——在同一轮对话中,用户可以继续问"那第二高的呢?",AI 能理解上下文。
提示:可以用一个列表保存历史问题和 SQL,在 prompt 中带上上一轮的 SQL 帮助 AI 理解"第二高"指什么。
改造 2(进阶): 改造 get_schema() 支持 MySQL——用 pymysql 连接 MySQL 数据库,获取表结构的方式改为 SHOW COLUMNS FROM table_name。
提示:安装 pymysql>=1.1.0,connect = pymysql.connect(host, user, password, database),其余流程不变。
改造 3(挑战): 加一个"SQL 审计日志"功能——把每次生成的 SQL、执行结果、AI 解释保存到日志文件 nl2sql_log.md,按时间排列,方便回溯。
提示:在 ask() 函数末尾追加写入 Markdown 文件,格式为 ## 时间 – 问题 → SQL → 结论。
下期预告
Day 12 · 可视化 + AI:让 AI 帮你选图表
数据查出来了,怎么画图?柱状图还是折线图?本篇让 AI 根据数据特征自动推荐图表类型,再用 matplotlib 一键生成。三种数据场景实战:销量对比、趋势变化、占比构成。
📚 资源与工具(文末合规集中)
- Python sqlite3 文档: https://docs.python.org/3/library/sqlite3.html
- SQLite 语法教程: https://www.sqlite.org/lang.html
- 智谱开放平台(GLM,免费 API Key): https://open.bigmodel.cn/
- GLM 模型文档: https://docs.bigmodel.cn/
- 本专栏配套代码: CSDN 下载区(每篇更新)
🍃 作者的话
梅雅达编程笔记,专注 Python + AI 办公自动化实战教程。
NL2SQL 不是要替代数据库管理员,而是让不会写 SQL 的人也能快速查数据。AI 帮你翻译、帮你解读,你只需要知道"我想看什么"——剩下的交给代码。
往期回顾
Day 01 · 为什么 2026 年办公自动化得“AI 化“了
Day 02 · 环境搭建:一套装备打天下
Day 03 · Prompt 工程 5 大心法
Day 04 · LLM API 横评 已被AI编程社区收录
Day 05 · 让 AI 输出结构化数据:告别“偶尔给你一段散文“
Day 06 · Excel + AI:让电子表格变“聪明“
Day 07 · Word + AI:自动生成文档
Day 08 · PDF 智能提取:让 AI 读懂合同、发票、简历
Day 09 · PPT 自动生成:文字大纲变演示文稿
Day 10 · 邮件 + AI:智能分类与自动回复
专栏推荐
- 「AI Python 系列」第 01 栏 · AI+办公自动化(连载中)
- 「AI Python 系列」第 02 栏 · AI+数据可视化(连载中)
- 「AI Python 系列」第 03 栏 · Python 爬虫实战(连载中)
资源领取
- 关注作者获取本栏完整代码和数据集
原创声明:本文为梅雅达编程笔记原创作品,未经允许不得转载。

