DeepSeek总结的AI 能写 SQL 吗
AI 能写 SQL 吗?
Bruce Momjian
https://momjian.us/presentations
知识共享署名许可
最后更新:2026 年 7 月
背景
我之前的演讲《使用 Postgres 构建 MCP 服务器》涵盖了 MCP 和固定查询的 SQL 函数。
https://momjian.us/main/presentations/ai.html#mcp
大纲
- 是的,它能
- 数据库设置
- 机制
- 一般文本查询
- 数据查询
- 局限性
1. 是的,它能:以 JSON 表示的数据
为每个查询使用以下 RAG 数据:
{"order_id": 735, "product": "gears", "amount": 21, "inv_date": "2024-04-12"}
{"order_id": 4423, "product": "pencils", "amount": 13, "inv_date": "2024-02-05"}
{"order_id": 1334, "product": "glue", "amount": 5, "inv_date": "2024-03-25"}
https://momjian.us/main/presentations/ai.html#trenches
SQL 查询可以被生成
什么 SQL 查询会生成最新的发票日期?
要从表中生成最新的发票日期(假设表名为 ‘invoices’),你可以使用以下 SQL 查询:
SELECT MAX(inv_date) AS latest_invoice_date
FROM invoices;
数据库模式必须从 JSON 数据中猜测。
2. 数据库设置
选择了 Pagila 示例数据库:
- 视频租赁店
- 72 MB
- 15 张表
- 外键
- 未使用
- JSON
- 语义/向量搜索
- 分区
https://github.com/devrimgunduz/pagila
下载示例数据库
# 下载 Pagila 示例数据库
rm -rf pagila-master master.zip
wget -q https://github.com/devrimgunduz/pagila/archive/refs/heads/master.zip
unzip master.zip
# 安装 pgvector 二进制文件
cd /u/src/gen/pgvector/pgvector/
git pull origin
make clean
make install
cd -
# 创建 pagila 数据库
su postgres -c 'dropdb pagila'
su postgres -c 'createdb pagila'
https://github.com/pgvector/pgvector
安装示例数据库
su postgres <<END
export PGUSER='postgres'
export PGDATABASE='pagila'
# 安装 pgvector 扩展
psql -c 'CREATE EXTENSION VECTOR' pagila
# 加载数据库
psql -f pagila-master/pagila-schema.sql
psql -f pagila-master/pagila-data.sql
psql -f pagila-master/pagila-schema-jsonb.sql
pg_restore -U postgres -d pagila pagila-master/pagila-data-apt-jsonb.backup
安装示例数据库(续)
# 删除未使用的表
psql -c 'DROP TABLE payment CASCADE;'
psql -c 'DROP TABLE packages_apt_postgresql_org;'
psql -c 'DROP TABLE packages_yum_postgresql_org;'
# 启用查询日志
psql -c "ALTER SYSTEM SET log_statement = 'all';"
psql -c "SELECT pg_reload_conf();"
END
Pagila 的表
\dt
List of tables
Schema | Name | Type | Owner
--------+-----------------------------+-------+----------
public | actor | table | postgres
public | address | table | postgres
public | category | table | postgres
public | city | table | postgres
public | country | table | postgres
public | customer | table | postgres
public | film | table | postgres
public | film_actor | table | postgres
public | film_category | table | postgres
public | film_embedding | table | postgres
public | inventory | table | postgres
public | language | table | postgres
public | rental | table | postgres
public | staff | table | postgres
public | store | table | postgres
Pagila 模式
https://dbdiagram.io
Pagila 主表
Pagila 电影表
Pagila 商店表
3. 机制
将文本查询转换为 SQL 查询需要访问:
- 表名
- 列名
- 列约束
- PRIMARY KEY、UNIQUE
- 外键、REFERENCES
- CHECK
- 对象注释,特别是列注释 *
- 数据库和模式名称
- 表数据
https://www.youtube.com/watch?v=S5ezVVJhQmE
- https://momjian.us/main/blogs/pgblog/2026.html#March_16_2026
RAG
在系统上下文中传递数据库模式,例如 pg_dump --schema-only
生成式 AI 提示
| 系统 | 用户 |
|---|---|
| 上下文 | 用户请求 |
| 限制 | |
| 个性 |
https://blog.promptlayer.com/system-prompt-vs-user-prompt-a-comprehensive-guide-for-ai-prompts/
MCP
Postgres MCP Pro 提供的工具
- 对象信息
- list_schemas()
- list_objects()
- get_object_details()
- SQL
- execute_sql()
- 性能分析
- explain_query()
- get_top_queries()
- analyze_workload_indexes()
- analyze_query_indexes()
- analyze_db_health()
https://github.com/crystaldba/postgres-mcp
https://momjian.us/main/presentations/ai.html#mcp
SQL 访问
使用 Claude Code CLI、Opus 5.5
- 在命令 shell 中运行
- 直接通过 psql 发出 SQL 查询
- 需要付费订阅或按 token 定价
- 不是 Claude Desktop 的 Claude Code 选项
https://code.claude.com/docs/en/quickstart
https://www.iwoszapar.com/p/claude-code-cli-vs-desktop
强大的 SQL
Claude Code 通过 psql 可以查询和修改
- 数据库状态,例如数据库版本和存储大小
- 对象属性,例如数据库模式
- 用户数据
4. 一般文本查询
告诉我关于本地托管的 pagila 数据库。
pagila 是 MySQL 的 Sakila 示例 DVD 租赁数据库的 PostgreSQL 移植版。这里的副本与标准版本相比发生了很大变化:它缺少 payment 表,某些表有更多数据,并添加了几张额外的表。
服务器
- PostgreSQL devel,一个开发构建。它从 /usr/local/pgsql 运行,数据在 /u/pgsql/data。
- 数据库为 72 MB,所有者为 postgres,一切都在 public 模式中。
- 没有 claude 数据库角色。我以 postgres(超级用户)身份无密码进入,只运行了只读查询。
发出的 SQL 查询:PostgreSQL 版本
Claude Code 从命令行运行 psql:
psql -d pagila -c "select version();"
数据库大小
-- 内部查询
select pg_size_pretty(pg_database_size('pagila')) size,
pg_catalog.pg_get_userbyid(datdba) owner
from pg_database
where datname='pagila'
关系列表
-- 内部查询
select n.nspname, c.relname, c.relkind, c.reltuples::bigint est_rows,
pg_size_pretty(pg_total_relation_size(c.oid)) size
from pg_class c join pg_namespace n on n.oid=c.relnamespace
where n.nspname not in ('pg_catalog','information_schema','pg_toast') and
c.relkind in ('r','p','v','m')
order by c.relkind, pg_total_relation_size( c.oid) desc
其他查询主题
- 获取匹配的模式
- 获取匹配的已安装扩展
- 获取匹配的函数
- 获取匹配的类型
- 获取匹配的要描述的关系
- 获取关于一个关系的一般信息
- 获取一个关系的每列信息
- 获取此表的索引
- 获取此表的外键约束
- 获取引用此表的外键
- 获取此表的行级策略
- 获取此表的扩展统计信息
- 获取发布此表的发布
- 获取排除此表的发布
- 获取此关系的触发器
- 获取继承父表
- 获取子表
5. 数据查询:员工
哪位员工卖出了最多的视频租赁?显示 SQL 查询。
SELECT s.staff_id, s.first_name, s.last_name,
s.store_id, count(*) AS rentals
FROM rental r JOIN staff s USING (staff_id)
GROUP BY s.staff_id
ORDER BY rentals DESC
LIMIT 5;
商店查询
哪家商店卖出了最多的视频租赁?显示 SQL 查询。
SELECT i.store_id, count(*) AS rentals
FROM rental r JOIN inventory i USING (inventory_id)
GROUP BY i.store_id
ORDER BY rentals DESC
LIMIT 5;
这是基于库存商店而非员工商店的报告,可能假设员工偶尔会在不同的商店工作。
电影类型查询
哪种电影类型的视频租赁最少?显示 SQL 查询。
SELECT c.category_id, c.name AS genre, count(r.rental_id) AS rentals
FROM category c LEFT JOIN film_category fc USING (category_id)
LEFT JOIN inventory i USING (film_id)
LEFT JOIN rental r USING (inventory_id)
GROUP BY c.category_id, c.name
ORDER BY rentals ASC, genre;
演员查询
哪位演员出演了我们库存中最多的电影?显示 SQL 查询。
SELECT a.actor_id, a.first_name, a.last_name,
count(DISTINCT fa.film_id) AS films_in_inventory
FROM actor a JOIN film_actor fa USING (actor_id)
WHERE EXISTS (SELECT 1 FROM inventory i WHERE i.film_id = fa.film_id)
GROUP BY a.actor_id
ORDER BY films_in_inventory DESC
LIMIT 5;
客户查询
哪位客户在 2022 年租了最多的视频?显示 SQL 查询。
SELECT c.customer_id, c.first_name, c.last_name, count(*) AS rentals_2022
FROM rental r JOIN customer c USING (customer_id)
WHERE r.rental_date >= '2022-01-01' AND
r.rental_date < '2023-01-01'
GROUP BY c.customer_id
ORDER BY rentals_2022 DESC
LIMIT 5;
视频租赁查询
哪些视频在 2023 年从未被租过?显示 SQL 查询。
SELECT f.film_id, f.title,
EXISTS (SELECT 1
FROM inventory i
WHERE i.film_id = f.film_id) AS in_inventory
FROM film f
WHERE NOT EXISTS (SELECT 1
FROM rental r JOIN inventory i USING (inventory_id)
WHERE i.film_id = f.film_id AND
r.rental_date >= '2023-01-01' AND
r.rental_date < '2024-01-01')
ORDER BY in_inventory DESC, f.title;
不清楚“视频”是指所有电影还是仅指库存中的电影,因此输出按库存状态排序。
语言查询
哪种视频语言从 2022 年到 2023 年的绝对(而非百分比)租赁增长最高?显示 SQL 查询。
SELECT l.name AS language,
count(*) FILTER (WHERE r.rental_date >= '2022-01-01' AND
r.rental_date < '2023-01-01') AS rentals_2022,
count(*) FILTER (WHERE r.rental_date >= '2023-01-01' AND
r.rental_date < '2024-01-01') AS rentals_2023,
count(*) FILTER (WHERE r.rental_date >= '2023-01-01' AND
r.rental_date < '2024-01-01') -
count(*) FILTER (WHERE r.rental_date >= '2022-01-01' AND
r.rental_date < '2023-01-01') AS change
FROM language l LEFT JOIN film f ON f.language_id = l.language_id
LEFT JOIN inventory i ON i.film_id = f.film_id
LEFT JOIN rental r ON r.inventory_id = i.inventory_id
GROUP BY l.language_id, l.name
ORDER BY change DESC;
逾期租赁查询
哪些客户的视频逾期最严重?显示 SQL 查询。
WITH overdue AS (
SELECT r.customer_id, f.title, current_date -
(r.rental_date + f.rental_duration * interval '1 day')::date AS days
FROM rental r JOIN inventory i USING (inventory_id)
JOIN film f USING (film_id)
WHERE r.return_date IS NULL AND
r.rental_date + f.rental_duration * interval '1 day' < now() )
SELECT c.customer_id AS id,
c.first_name || ' ' || c.last_name AS customer,
c.email,
count(*) AS n_overdue,
max(days) AS max_days,
sum(days) AS total_days,
string_agg(title, ', ' ORDER BY days DESC) AS titles
FROM overdue JOIN customer c USING (customer_id)
GROUP BY 1, 2, 3
ORDER BY max_days DESC, n_overdue DESC, total_days DESC
LIMIT 12;
向商店添加海拔
ALTER TABLE store ADD altitude INTEGER;
UPDATE store SET altitude = random(0, 4000);
海拔查询
哪家商店海拔最高?显示 SQL 查询。
-- 内部查询
select table_name||'.'||column_name||' '||data_type
from information_schema.columns
where table_schema='public' and
table_name in ('store','address','city','country')
select table_name||'.'||column_name
from information_schema.columns
where table_schema='public' and
column_name ~* 'alt|elev|height|lat|lon|geo|loc'
海拔查询(续)
select col_description('store'::regclass,
(select attnum
from pg_attribute
where attrelid='store'::regclass and attname='altitude'))
SELECT s.store_id, s.altitude, a.address, c.city, co.country
FROM store s JOIN address a USING (address_id)
JOIN city c USING (city_id)
JOIN country co USING (country_id)
ORDER BY s.altitude DESC NULLS LAST
LIMIT 5;
没有给出单位。该列没有描述。所有 500 家商店的值从 1 到 4000,暗示是米。
添加 SQL 注释
COMMENT ON COLUMN store.altitude IS 'units are in feet';
海拔查询(带注释)
哪家商店海拔最高?显示 SQL 查询。
-- 内部查询
select col_description('store'::regclass,
(select attnum
from pg_attribute
where attrelid='store'::regclass and attname='altitude'))
海拔查询(带注释,续)
SELECT s.store_id,
s.altitude AS altitude_ft,
round(s.altitude * 0.3048) AS altitude_m,
a.address, c.city, co.country
FROM store s JOIN address a USING (address_id)
JOIN city c USING (city_id)
JOIN country co USING (country_id)
ORDER BY s.altitude DESC NULLS LAST
LIMIT 5;
使用 AI 添加 SQL 注释
向商店的海拔列添加 SQL 注释,说明单位是英尺。显示 SQL 查询。
-- 内部查询
COMMENT ON COLUMN store.altitude IS 'Altitude of the store, in feet';
6. 局限性
真实世界的数据库很复杂:
- 更多的对象和行
- 对象名称不太一致
- 数据不太一致
- 缺少约束
此外,我的示例大多是只读的,没有安全控制。
基准测试历史
2017:WikiSQL
2018:Spider
2023:BIRD
2024:Spider 2.0
2026:BIRD-Verified
https://www.youtube.com/watch?v=Hty5YMwvMsQ
https://www.flickr.com/photos/187543158@N07/
提高准确性
- 对象名称
- 引用完整性
- SQL 注释
- 数据一致性
- LLM 提示
- 机器学习
结论
https://momjian.us/presentations
https://www.flickr.com/photos/gozalewis/
更多推荐

所有评论(0)