SQL 查询生成 Prompt:从自然语言到优化 SQL
描述查询需求与表结构,AI 输出可执行 SQL 并解释执行计划的关键关注点,同时给出索引建议,以及分页、去重、N+1 等常见陷阱的处理方式,避免全表扫描,并附改写前后的写法对比。
发布 2026-06-21更新 2026-10-14用法
把表结构(DDL)和你的需求描述粘进来,AI 会生成带注释的 SQL + 性能建议。
什么时候用 / 别用
| 场景 | 用不用 | 原因 |
|---|---|---|
| 即席查询 / 报表取数 | 用 | 省掉翻字典的时间,AI 对聚合与窗口函数很熟 |
| 把慢查询改写成等价快查询 | 用 | 但必须用 EXPLAIN ANALYZE 验证,不要信「应该更快」 |
| 生成索引 / DDL 变更建议 | 慎用 | 索引会锁表、会写磁盘,建议先给语句,人工审完再执行 |
生成 DELETE / UPDATE / DROP | 别直接用 | 必须先生成只读的 SELECT 预演命中行数,确认范围再执行 |
| 涉及权限、脱敏、合规的查询 | 别用 | AI 不知道你的行级权限策略与脱敏规则 |
一条硬纪律:任何 AI 生成的写操作,都先在事务里跑
SELECT看命中范围,再决定执不执行。BEGIN; … ROLLBACK;是你的安全网。
Prompt
你是一个资深 DBA。请根据以下表结构,把我描述的需求转成 SQL 查询。
## 表结构
{{粘贴你的 CREATE TABLE 语句,或表名+字段说明}}
## 需求
{{用自然语言描述你要查什么,比如"查上个月销售额前十的用户"}}
## 要求
1. 生成标准 SQL(MySQL / PostgreSQL,说明你选了哪个方言)
2. 所有字段名用反引号包裹
3. 每个关键步骤加注释说明逻辑
4. 如果需要 JOIN,说明为什么选这种 JOIN 类型
5. 如果查询可能慢,给出索引建议
6. 如果需求有歧义,先列出你的假设,再给 SQL
## 输出格式
### 假设(如果有歧义)
- 假设 1:...
- 假设 2:...
### SQL
```sql
-- 你的 SQL
```
### 索引建议(如果需要)
- `CREATE INDEX idx_xxx ON table(column)` — 理由
### 性能说明
- 预估扫描行数:...
- 是否走索引:...
示例对话
输入:
表结构:orders(id, user_id, amount, created_at, status)
需求:查上个月每个用户的总消费,取前 10 名
输出:
假设
- "上个月"指自然月(非最近 30 天)
- 只统计已支付订单(status = 'paid')
SQL
SELECT
user_id,
SUM(amount) AS total_spent
FROM `orders`
WHERE `status` = 'paid'
AND `created_at` >= DATE_FORMAT(DATE_SUB(CURDATE(), INTERVAL 1 MONTH), '%Y-%m-01')
AND `created_at` < DATE_FORMAT(CURDATE(), '%Y-%m-01')
GROUP BY `user_id`
ORDER BY `total_spent` DESC
LIMIT 10;
索引建议
CREATE INDEX idx_status_created ON orders(status, created_at)— 覆盖时间范围筛选
性能说明
- 走 idx_status_created 索引,避免全表扫描
- GROUP BY user_id 需要临时表排序,数据量大时考虑分页
为什么有效
- 给表结构(DDL)而不是表名:AI 不知道列名就会编,编出来的 SQL 能跑但查的是错的字段。
- 要求说明索引影响:绝大多数慢查询不是 SQL 写法问题而是索引没命中,不显式要求,AI 不会主动看执行计划。
- 要求给边界条件:空结果、NULL 语义、时区——这三类是 SQL 输出与预期不符的高频来源。
验收判据:四条过了才算能上
- 执行计划实测 —— 跑
EXPLAIN ANALYZE,确认走的是预期索引,没有意外的Seq Scan落在千万级大表上。 - 边界语义确认 —— 空结果集、NULL 的三值逻辑、时区(
timestamptz还是本地时间)、「上个月」是自然月还是滚动 30 天。这四类是 SQL 结果与预期不符的高频来源。 - 结果行数对得上 —— 用一个你自己手算得出数字的小样本先跑一遍,对不上说明 JOIN 扇出或多对多关系被处理错了。
- 写操作先只读预演 ——
UPDATE/DELETE先写成SELECT count(*)跑一遍,确认命中行数在预期量级。
第 3 条最常被跳过,也最容易出事。 AI 写的 JOIN 在语义上「说得通」,但如果关联键不是唯一的,结果集会被静默放大——查出来的总数看着合理,实际每一行都被重复计了。
进阶(自动化)
生成后先在只读副本上验证执行计划,再上生产:
# 只看计划不执行
pnpm exec prisma db execute --url "$READONLY_URL" \
--stdin <<< "EXPLAIN ANALYZE <生成的 SQL>"
关注 Seq Scan 是否落在预期索引上;长期方案见 Prompt 缓存 之外更直接的手段——加索引与改查询结构。
四个变体:按你的目的选一个
| 变体 | 追加指令 | 适用 |
|---|---|---|
| A 只读取数 | 「只生成 SELECT,禁止任何写操作;输出预估扫描行数」 | 报表、即席分析 |
| B 性能优化 | 「先给当前 SQL 的瓶颈判断,再给改写方案,并说明每条改写依赖哪个索引」 | 慢查询治理 |
| C 方言迁移 | 「把这段 SQL 从 <源方言> 迁到 <目标方言>,逐条列出不兼容的语法与替代写法」 | MySQL ↔ PostgreSQL 迁移 |
| D 安全预演 | 「先给等价的 SELECT 统计命中行数,确认无误后再给写操作,并包进事务」 | 任何 UPDATE / DELETE |
接进管道(headless)
# 把 DDL 喂进去,直接拿到带执行计划说明的 SQL
cat schema.sql | claude -p "按 /prompts/sql-generation 的模板生成查询,
采用变体 B(性能优化),方言 PostgreSQL,并说明每条索引建议的代价"
方言必须在管道命令里写死。AI 默认会给你最通用的写法,而 MySQL 与 PostgreSQL 在日期函数、字符串拼接、
LIMIT语法上的差异足以让 SQL 直接报错。
反例(AI 默认会写的烂版本)
默认输出:SELECT * FROM orders WHERE created_at > '2026-01-01'——没走索引提示、没处理 created_at 为 NULL 的行、也没说时区是 UTC 还是本地。
加了 prompt 之后:明确 created_at 上有 B-tree 索引故可走 range scan;WHERE created_at >= '2026-01-01'::timestamptz AND created_at IS NOT NULL;并提示「若要用本地时区,先确认列类型为 timestamptz」。
延伸阅读
- 性能优化 Prompt — 慢查询定位与索引优化
- Regex 生成 Prompt — 同样"自然语言 → 代码"的套路
- Cursor + MCP 操作数据库 — 让 AI 直接连库查 schema、跑 EXPLAIN