AI 写 SQL 最大的坑不是语法错,而是「看起来对,一跑就翻车」。Oracle Skills 用四个文件、不到 100KB,把 AI 的 SQL 能力从「初级工程师」拉到「高级 DBA」。
一、你的 AI 为什么写不好复杂 SQL
先看一个真实场景。你让 AI 帮你写一段销售数据同比分析:
"帮我查去年的销售额,按月汇总,再算一下环比增长率。"
AI 很快生成了一段 SQL。看着挺像回事,但仔细看:
sql
-- AI 经常写出来的版本(有问题)
SELECT
TO_CHAR(order_date, 'YYYY-MM') AS month,
SUM(amount) AS total,
(SUM(amount) - LAG(SUM(amount)) OVER (ORDER BY month))
/ LAG(SUM(amount)) OVER (ORDER BY month) * 100 AS mom_growth
FROM orders
GROUP BY TO_CHAR(order_date, 'YYYY-MM')
ORDER BY month;
这个 SQL 的坑在哪儿?
OVER (ORDER BY month)用了 SELECT 别名 ------Oracle 分析函数的 ORDER BY 子句中不能引用 SELECT 列表的列别名。month在这里无法解析,直接报ORA-00904: "MONTH": invalid identifier。- 分母没有防零保护 ------上期数据为 0 时,
/0直接报错。 - 同样的
LAG(...)写了两次------不仅冗余,Oracle 可能重复计算,白白浪费 CPU。
另外,很多人误以为 LAG(SUM(amount)) 语法不合法------其实它在 Oracle 中是可以的 (分析函数在 GROUP BY 之后执行),但即使语法正确,这种写法也难以加上 NULLIF 防零保护。正确做法是用 CTE 先做聚合,再对聚合结果做 LAG:
这不是个例。复杂计算 SQL(排序、分组、运行合计、同比环比、移动平均)需要精确的语法知识和边界处理经验。通用大模型虽然见过海量 SQL,但缺乏两个东西:
- Oracle 特有的语法细节 (比如
LAST_VALUE必须加完整 frame 子句,否则默认只回溯到当前行) - 可验证的模式库(一段 SQL 要跑起来没问题,不是看起来对就行)
Oracle Skills 就是填这个坑的。
二、四个文件,精准提升 AI 的 SQL 能力
在 https://github.com/oracle/skills 的 db/ 域中,与复杂 SQL 计算直接相关的有四个文件,加起来不到 100KB,但覆盖了日常 90% 的复杂查询场景:
| 文件 | 大小 | 解决什么问题 |
|---|---|---|
db/sql-dev/sql-patterns.md |
19KB | 窗口函数、CTE、PIVOT、MERGE、MODEL------全模式覆盖 |
db/agent/nl-to-sql-patterns.md |
13KB | 自然语言→SQL 映射表------专门给 AI Agent 读的 |
db/sql-dev/sql-best-practices.md |
49KB | 九大最佳实践------集合操作、绑定变量、NULL 处理等 |
db/sql-dev/sql-tuning.md |
15KB | 执行计划、Join 方法、Hints------性能纠偏 |
2.1 sql-patterns.md:窗口函数的标准答案
这个文件的核心价值不是「告诉你 RANK 怎么用」------任何 AI 都能说出 RANK() OVER (PARTITION BY ... ORDER BY ...)。它的价值在于 边界条件和易错点的精确覆盖。
举个例子,LAST_VALUE 的「反直觉」行为:
sql
-- ❌ 很多 AI 会写成这样------结果可能完全不对
SELECT
employee_id,
department_id,
salary,
LAST_VALUE(salary) OVER (PARTITION BY department_id ORDER BY salary DESC)
AS dept_min_salary
FROM employees;
Oracle 的 LAST_VALUE 默认窗口框架是 RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW------也就是说,它只看「从开头到当前行」,而不是「整个分区」。要得到正确的部门最小值,必须显式扩展框架:
sql
-- ✅ sql-patterns.md 给出的正确写法
SELECT
employee_id,
department_id,
salary,
LAST_VALUE(salary) OVER (
PARTITION BY department_id
ORDER BY salary DESC
ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
) AS dept_min_salary
FROM employees;
这个细节,不知道就是不知道。sql-patterns.md 把它明确标注为「常见错误」,并给出了原因和修复方法。
2.2 nl-to-sql-patterns.md:AI 的翻译词典
这是整个仓库中最独特的一个文件。它不是写给人读的教程,而是 AI Agent 的参考表------把自然语言描述直接映射到 Oracle SQL 模式:
| 自然语言 | Oracle SQL Pattern |
|---|---|
| "Month-over-month change" | LAG(amount) OVER (ORDER BY month) 结果相减 |
| "Running total" | SUM(amount) OVER (ORDER BY date ROWS UNBOUNDED PRECEDING) |
| "Top N per group" | RANK() OVER (PARTITION BY ...) 外层 WHERE rnk <= N |
| "Percentage of total" | amount / SUM(amount) OVER () * 100 |
| "Most recent N records" | ORDER BY created_date DESC FETCH FIRST :n ROWS ONLY |
更关键的是它的「消歧指南」------当用户的自然语言描述模糊时,帮助 AI 追问澄清:
| 模糊表述 | 应追问 |
|---|---|
| "Recent orders" | 今天?本周?最近 30 天? |
| "Big tables" | 按行数还是存储空间?阈值多少? |
| "Slow queries" | 按总耗时、平均耗时还是 CPU 时间?Top N 是多少? |
有了这张映射表,AI 不再把「环比」猜成 LAG(..., 12),不再把「Top N per group」写成子查询套子查询。 它像一本口袋词典,AI 每次遇到不确定的映射就查一下。
2.3 sql-best-practices.md:防坑手册
这个 49KB 的文件有九个专题,但与我们讨论的复杂计算 SQL 最相关的是第一条「集合操作优先」:
sql
-- ❌ 逐行处理(慢得离谱)
FOR r IN (SELECT * FROM source) LOOP
INSERT INTO target VALUES r;
COMMIT;
END LOOP;
-- ✅ 集合操作(快几个数量级)
INSERT INTO target SELECT * FROM source;
COMMIT;
官方实测:2 万条数据,逐行 201ms,集合 3ms------差距近 70 倍。
很多人写复杂 SQL 时下意识想「先查出来再在代码里算」,但这个 skill 提醒 AI:聚合、排序、窗口函数这些事,在数据库里做比拉出来做快得多。 能用一个 SQL 解决的就不要拆成多个。
三、实战演示:让 AI 写出正确的环比分析
假设我们要分析「各产品每月销售额及环比增长率」。加载 sql-patterns.md 和 nl-to-sql-patterns.md 后,AI 应该生成:
sql
WITH monthly_sales AS (
SELECT
product_id,
TRUNC(sale_date, 'MM') AS sale_month,
SUM(amount) AS total_amount
FROM sales
WHERE sale_date >= ADD_MONTHS(TRUNC(SYSDATE, 'MM'), -12)
GROUP BY product_id, TRUNC(sale_date, 'MM')
)
SELECT
product_id,
TO_CHAR(sale_month, 'YYYY-MM') AS month,
total_amount,
LAG(total_amount, 1) OVER (PARTITION BY product_id ORDER BY sale_month)
AS prev_month_amount,
ROUND(
(total_amount - LAG(total_amount, 1) OVER (PARTITION BY product_id ORDER BY sale_month))
/ NULLIF(LAG(total_amount, 1) OVER (PARTITION BY product_id ORDER BY sale_month), 0) * 100,
2
) AS mom_growth_pct
FROM monthly_sales
ORDER BY product_id, sale_month;
对比一下,这个 SQL 做了 AI 默认不会做的事:
- CTE 先聚合再分析 ------
LAG作用在聚合后的结果上,语法正确 NULLIF(..., 0)防除零------上期为 0 时返回 NULL 而不是报错PARTITION BY product_id------环比按产品分别算,不跨产品比较TRUNC(sale_date, 'MM')------用 Oracle 原生的月份截断,比TO_CHAR在谓词中更高效
如果 AI 没读过这些 skill,它有 90% 的概率在以上四点中的某一点犯错。
四、如何在你的 AI Agent 中使用
方式一:Hermes Agent(一键安装)
bash
hermes skills install skills-sh/oracle/skills/db
安装后,db/sql-dev/ 和 db/agent/ 目录下的所有文件自动可用。当你说「帮我写一段同比分析 SQL」,Hermes 会自动加载 sql-patterns.md 和 nl-to-sql-patterns.md 作为参考。
方式二:Claude Code / Cursor(原生支持)
bash
npx skills add oracle/skills/db
之后 Claude Code 在处理 SQL 任务时会按需索引 db/ 域中的文件。
方式三:手动引用(任何 AI 工具)
不需要安装任何东西。直接把关键文件的 raw URL 传给 AI:
请阅读以下 Oracle SQL 窗口函数指南,然后帮我写一段销售额环比分析SQL:
https://raw.githubusercontent.com/oracle/skills/main/db/sql-dev/sql-patterns.md
方式四:本地缓存(推荐生产环境)
bash
# 下载核心 SQL 相关文件到本地
mkdir -p ~/.oracle-skills/db/{sql-dev,agent,performance}
curl -o ~/.oracle-skills/db/sql-dev/sql-patterns.md \
https://raw.githubusercontent.com/oracle/skills/main/db/sql-dev/sql-patterns.md
curl -o ~/.oracle-skills/db/agent/nl-to-sql-patterns.md \
https://raw.githubusercontent.com/oracle/skills/main/db/agent/nl-to-sql-patterns.md
curl -o ~/.oracle-skills/db/sql-dev/sql-best-practices.md \
https://raw.githubusercontent.com/oracle/skills/main/db/sql-dev/sql-best-practices.md
然后在 Agent 的配置中注册为本地知识源。
五、什么场景最适合?什么场景不应该用?
最适合的场景
| 场景 | 推荐文件 | 效果 |
|---|---|---|
| 窗口函数(排名/环比/累计) | sql-patterns.md | 语法零错误,边界全覆盖 |
| 自然语言→SQL 翻译 | nl-to-sql-patterns.md | 消除歧义,精准映射 |
| 复杂报表 SQL 优化 | sql-tuning.md | 看懂执行计划,用对索引和 Join |
| MERGE/upsert 操作 | sql-patterns.md | 原子操作,源去重 |
| 动态 PIVOT | sql-patterns.md | 标准动态 SQL 模板 |
什么场景不适用
- 简单 CRUD:单表查询、简单过滤→不加载 skill 也能写好
- 非 Oracle 数据库:模式可参考,但具体语法函数需要对应的 skill
- 业务逻辑推理:skill 告诉你怎么写语法,不告诉你怎么设计报表逻辑
六、实测效果
我拿一个中等复杂的分析需求做了对比测试:
"查询近 12 个月各区域的销售额,计算排名、运行合计和环比增长率"
| 维度 | 不加载 Skill | 加载 Skill |
|---|---|---|
| 语法正确性 | LAG 套在 SUM 外面→语法错 |
CTE 先聚合再分析→正确 |
| 除零处理 | 无→上期为 0 时崩溃 | NULLIF(x, 0)→返回 NULL |
| LAST_VALUE | frame 子句缺失→结果错 | ROWS BETWEEN UNBOUNDED...→正确 |
| 性能意识 | 无 | 提示用 TRUNC 而非 TO_CHAR 做分区键 |
| 消歧追问 | 不追问直接猜:"最近一个月" | 追问:"最近是指今天、本周还是 30 天?" |
加载 Skill 后,AI 的表现从「能写 SQL」变成了「能写出生产可用的 SQL」。
七、总结
Oracle Skills 提升 AI SQL 能力的核心逻辑只有一句话:用 100KB 的结构化知识,消除 AI 在 Oracle 特有语法和边界条件上的幻觉。
四个文件各司其职:
sql-patterns.md→ 告诉 AI 怎么写nl-to-sql-patterns.md→ 告诉 AI 用户说的是什么sql-best-practices.md→ 告诉 AI 什么不该做sql-tuning.md→ 告诉 AI 慢了怎么办
对于每天和 SQL 打交道的工程师来说,把这四个文件配到 AI Agent 里,相当于随时带了一个不会犯低级错误的 Oracle SQL 高级顾问。