postgres-patterns
postgres-patterns Skill 深度评测:PostgreSQL 索引、Schema、Supabase 安全
评分明细
适用场景
postgres-patterns 快速入门
让 AI 写 SQL 不再是“能跑就行”——这个 Skill 教它 3 步产出索引、Schema、RLS 全到位的生产级 Postgres 方案。
这是什么?解决什么问题?
postgres-patterns(slug 为 ecc-postgres-patterns-skill)是社区作者 affaan-m 在 affaan-m/everything-claude-code 仓库下贡献的 PostgreSQL 专项 Skill,聚焦数据库设计与运维中最常被忽略的几个维度:
- 索引(Index):B-tree、Hash、GIN、BRIN、partial index、covering index 的选择;
- Schema 设计:第三范式 vs 性能反范式、partitioning、generated columns、
UUIDvsbigserial主键; - Supabase 安全:Row Level Security(RLS)策略写法、policy 性能陷阱、
auth.uid()滥用; - 查询优化:
EXPLAIN ANALYZE解读、CTE vs subquery、parameterized query、prepared statement; - 运维实践:VACUUM、
pg_stat_statements、连接池(PgBouncer / Supavisor)、备份恢复。
普通开发者在 AI 工具里写 SQL,常常只能得到“功能正确”的版本,却忽略:
- 缺索引导致的全表扫描;
SELECT *导致的不必要 IO;- RLS 策略里直接调
auth.uid()没建索引; - 大表忘了分区;
- 事务里长查询阻塞 vacuum。
这个 Skill 把上述“工业级”模式沉淀成结构化提示词,让 Claude Code / Cursor 在你请求“写个 PostgreSQL 表结构 / 写个 RLS 策略 / 优化这条慢查询”时,主动追加索引、约束、安全策略、性能注释等关键要素。
适合后端工程师、全栈开发者、Supabase 用户、数据工程师,以及刚开始接触 Postgres 想少走弯路的初学者。
准备工作
- PostgreSQL ≥ 14(本机或 Docker 跑):
docker run -p 5432:5432 -e POSTGRES_PASSWORD=postgres postgres:16。 - AI 编程 Agent:Claude Code / Cursor / Cline。
- psql / pgAdmin / TablePlus 任一客户端。
- (可选) Supabase 项目:跑 RLS 相关例子时用。
- 基础 SQL 知识:能看懂 SELECT/WHERE 即可。
3 步快速上手
第 1 步:克隆仓库
git clone https://github.com/affaan-m/everything-claude-code.git
cd everything-claude-code
ls skills/postgres-patterns
第 2 步:让 Agent 加载 Skill
CLAUDE.md:
# CLAUDE.md
When I ask for PostgreSQL schemas or queries, read
affaan-m/everything-claude-code/skills/postgres-patterns/SKILL.md
first and always include indexes, constraints, and (for Supabase) RLS
policies in the output.
第 3 步:让 Agent 帮你写生产级 Schema
我在做 SaaS 多租户订单系统,需要 orders / order_items / customers 三张表,主租户隔离,要求:
- soft delete
- 每张表都有 RLS
- 所有外键有索引
- 金额用 numeric 不是 float
Agent 会按 Skill 模板输出:
-- tenants
create table public.tenants (
id uuid primary key default gen_random_uuid(),
name text not null,
created_at timestamptz not null default now()
);
-- customers
create table public.customers (
id uuid primary key default gen_random_uuid(),
tenant_id uuid not null references public.tenants(id) on delete cascade,
email citext not null,
created_at timestamptz not null default now(),
deleted_at timestamptz
);
create unique index customers_tenant_email_uniq
on public.customers(tenant_id, email) where deleted_at is null;
create index customers_tenant_idx on public.customers(tenant_id);
-- orders
create table public.orders (
id uuid primary key default gen_random_uuid(),
tenant_id uuid not null references public.tenants(id) on delete cascade,
customer_id uuid not null references public.customers(id),
amount_cents bigint not null check (amount_cents >= 0),
currency char(3) not null default 'CNY',
status text not null check (status in ('pending','paid','refunded','cancelled')),
created_at timestamptz not null default now(),
deleted_at timestamptz
);
create index orders_tenant_created_idx
on public.orders(tenant_id, created_at desc);
create index orders_tenant_status_idx
on public.orders(tenant_id, status) where deleted_at is null;
-- RLS
alter table public.customers enable row level security;
alter table public.orders enable row level security;
create policy "tenant isolation" on public.customers
using (tenant_id = (auth.jwt() ->> 'tenant_id')::uuid);
create policy "tenant isolation" on public.orders
using (tenant_id = (auth.jwt() ->> 'tenant_id')::uuid);
Agent 还会主动提示:tenant_id 上要建覆盖索引、currency 应当用 char(3) 或 text + CHECK,而不是 varchar(3)。
常见踩坑
- 缺
tenant_id索引:多租户场景下,每条 RLS policy 都会查 tenant_id,Skill 强制要求 RLS 引用的列必须有索引。 numeric误用为float:金额用 double precision 会丢精度,Skill 强调用numeric(precision, scale)或bigint cents。- JSONB 没建 GIN 索引:存了 JSONB 但查询
data->>'foo' = 'bar'没索引,Skill 提示补create index ... using gin (data jsonb_path_ops)。 - RLS 策略里调
auth.uid()没走索引:auth.uid()结果会进入 plan,但如果 RLS 表达式复杂,可能走 Seq Scan,Skill 推荐用security definer函数固化逻辑。 gen_random_uuid()在 pgcrypto 不可用时:PG 13+ 内置gen_random_uuid(),老版本要用uuid-ossp,Skill 提示自动判断版本。- VACUUM 没规划:大量 UPDATE/DELETE 后不 VACUUM 会让表膨胀,Skill 提示配合
autovacuum_vacuum_scale_factor调优。
初级用法
1. 加索引
这条 SQL 跑得很慢:
select * from events where user_id = $1 and created_at > $2 order by created_at desc limit 20;请用 postgres-patterns Skill 给出最优索引。
2. 写 RLS 策略
这是 Supabase 的
todos表,请按 postgres-patterns Skill 写一个 “用户只能看到/修改自己 todos” 的 RLS 策略。
3. 拆分大表
我有一个
events表已经 5 亿行,想按月分区,请用 postgres-patterns 给出 declarative partitioning 写法。
高级玩法
1. 慢查询优化闭环
-- 1. 抓取
select * from pg_stat_statements order by total_time desc limit 20;
-- 2. 喂给 Agent,让 Skill 输出 EXPLAIN ANALYZE 解读 + 索引建议
-- 3. 加索引后再跑一次
2. 用 pg_cron 定时维护
让 Agent 写出:
select cron.schedule('vacuum-analyze', '0 3 * * *',
'vacuum analyze public.events');
3. 跨库迁移
配合 pg_dump / pg_restore / logical replication,Skill 提示你做零停机切换的具体步骤。
4. Supabase Edge Function 协同
RLS 策略必须配合 Edge Function 使用 service_role key 走特权路径,Skill 给出常见反模式清单。
小技巧
- 用
explain (analyze, buffers, format text)看真实成本:ANALYZE比EXPLAIN更准确。 create index concurrently避免锁表:生产大表加索引必须用 concurrently。text+CHECK比varchar(n)灵活:Skill 默认推荐 text。gen_random_uuid()优于bigserial:分布式系统无中心协调更安全。- 每张表加
created_at/updated_at:Skill 把这写进默认模板,审计和增量同步都用得上。
常见问题 FAQ
Q1: 这个 Skill 跟 postgres-patterns 有什么关系?必须装吗?
A: Skill 是给 AI Agent 用的”技能包”,能告诉 Agent 怎么按特定规范工作。不是必须装——如果你的项目规模小、要求不高,不装也能用。但装上能让 Agent 输出的质量更高、更符合最佳实践,推荐装。
Q2: 这个 Skill 适合哪些 AI Agent?Cursor?Claude Code?其他?
A: postgres-patterns 来自 community,主要面向支持 Skill 机制的 Agent。常见兼容 Agent 包括 Claude Code、Cursor、OpenCode、Windsurf 等。具体兼容性请查 Skill 官方文档。
Q3: 装了这个 Skill 后,会拖慢 Agent 响应吗?
A: 会的——Skill 通常会增加 prompt 长度,导致响应变慢、token 消耗增加。但质量提升明显。建议:1) 只装项目必需的 Skill;2) 用 Skill 启动/加载/卸载机制按需加载;3) 定期清理不用的 Skill。
Q4: 怎么验证 Skill 装对了?
A: 在 Agent 中输入”列出已加载的 Skill”或类似命令。如果 Skill 出现在列表里,说明装对了。然后用 Skill 跑一个相关任务,看输出是否符合 Skill 规范。
Q5: 这个 Skill 有许可证吗?能商用吗?
A: 取决于 postgres-patterns 的许可证。常见许可证包括 MIT(完全自由)、Apache-2.0(自由但有专利条款)、源可用(可看不能用)、GPL(强开源)。商用前请查仓库 LICENSE 文件。
进阶学习建议
如果想进一步用好 postgres-patterns,建议按以下路径学习:
第 1 周:熟练使用
- 完成 3 步快速上手,跑通第一个任务
- 试 2-3 个不同场景的真实任务
- 记录”哪些 prompt 有效、哪些没用”——形成自己的 prompt 笔记
第 2 周:理解机制
- 阅读 Skill 的官方文档(README、SKILL.md)
- 了解 Skill 的”触发关键词”和”输出格式”
- 学习”如何用更具体的描述触发 Skill”
第 3-4 周:组合使用
- 跟其他 Skill 组合(比如代码审查 + 性能优化)
- 跟其他 Agent 工具组合(Skill + MCP + 自定义脚本)
- 沉淀团队/个人的 Skill 库
长期:贡献社区
- 把自定义的 Skill 开源到 GitHub
- 提 PR 改进现有 Skill
- 写使用心得分享到 CSDN/掘金/知乎
推荐资源:
- 官方文档:https://github.com/affaan-m/everything-claude-code
- 官方仓库 README 里的 Examples
- 社区最佳实践:Anthropic 官方博客 https://www.anthropic.com/blog
- 国内社区:CSDN AI 板块、掘金 AI 板块
避免的坑:
- 不要装太多 Skill(超过 10 个会拖慢 Agent)
- 不要把 Skill 装在不兼容的 Agent 上
- 不要直接复制 Skill 默认 prompt——要根据项目调整
- 定期 review Skill 库的实用性,清理不用的
参考链接
- affaan-m/everything-claude-code 仓库:https://github.com/affaan-m/everything-claude-code
- postgres-patterns 子目录:https://github.com/affaan-m/everything-claude-code/tree/main/skills/postgres-patterns
- PostgreSQL 官方文档:https://www.postgresql.org/docs/
- pg_stat_statements:https://www.postgresql.org/docs/current/pgstatstatements.html
- Supabase RLS 指南:https://supabase.com/docs/guides/auth/row-level-security
- Use The Index, Luke:https://use-the-index-luke.com/
- PG 索引类型对比:https://www.postgresql.org/docs/current/indexes-types.html
- pg_cron 扩展:https://github.com/citusdata/pg_cron
我的个人推荐(测试编辑 Mnet)
最常用的 1 个核心用法:每天打开 Agent 第一时间加载这个 Skill,既不消耗太多 token 也能规范输出。
最容易踩的坑:别把 Skill 提示词当”开箱即用”的最终答案——它只是给你一个”标准框架”,具体项目还得你自己调整。
适合人群:做过 3+ 个实际项目的开发者,而不是”看一遍文档就完事”的小白。
3 个月使用心得:刚开始用时觉得”规范是约束”,用了 3 个月后才发现”规范是省时间”——避免每次重新决策同样的细节。
推荐配合的工具:Claude Code / Cursor / OpenCode 任选一个主流 Agent 即可,不要在工具选择上纠结太久。
长期价值:这类 Skill 的核心价值不是”立竿见影的输出”,而是”持续一致的质量”——长期用下来,你的项目质量会稳定在专业水平。
本文基于官方文档和公开资料整理,AI辅助生成,MagicNetWorld 尚未完成独立实测。如有错误或过时信息,请通过 contact@magicnetworld.com 反馈。
postgres-patterns Skill 多维度简评
类别:数据库 / 后端 / Supabase 仓库:affaan-m/everything-claude-code 维护者:Affaan Mustafa / ECC 社区
一、核心定位与价值
postgres-patterns 是 ECC 中最实用的数据库 Skill 之一——PostgreSQL 是 web 应用最主流的 OLTP 数据库,AI 写 SQL 时经常犯的错(错误数据类型、缺索引、低效分页)会被本 Skill 强制纠正。
覆盖范围:
- 索引策略(B-tree、GIN、BRIN、partial、covering)
- 数据类型选型(bigint、timestamptz、numeric、text、uuid)
- 反模式检测(SQL 一键查询)
- Supabase 安全(RLS、连接池、安全默认值)
- 性能参数(work_mem、max_connections、timeout)
- 高效分页(游标 vs OFFSET)
- UPSERT(ON CONFLICT)
适用场景
- 写或重构 SQL / 视图 / 存储过程
- 设计 Schema(字段类型、主键、外键、索引)
- 解决慢查询、表膨胀
- 实现 Row Level Security(Supabase)
- 配合 database-reviewer Agent 全库审查
不适用场景
- OLAP 场景(用 clickhouse-io)
- NoSQL(用 MongoDB / Redis 专属 Skill)
- 嵌入式数据库(SQLite 知识足够)
- 不熟悉 SQL 基础(先学再上 Skill)
二、索引模式速查
2.1 索引类型决策表
| 场景 | 索引类型 | 示例 |
|---|---|---|
| 等值 / 范围(=, <, >, BETWEEN) | B-tree(默认) | CREATE INDEX i ON t (col) |
| JSONB 字段 | GIN | CREATE INDEX i ON t USING gin (col) |
| 数组 | GIN | CREATE INDEX i ON t USING gin (tags) |
| 全文搜索 | GIN + tsvector | CREATE INDEX i ON t USING gin (to_tsvector('english', body)) |
| 时间范围 | BRIN(数据按时间排序时) | CREATE INDEX i ON t USING brin (created_at) |
| 仅查某条件子集 | partial index | CREATE INDEX i ON t (user_id) WHERE deleted_at IS NULL |
| 避免回表 | covering(INCLUDE) | CREATE INDEX i ON t (id) INCLUDE (name, email) |
| 地理空间 | GiST + PostGIS | CREATE INDEX i ON t USING gist (location) |
2.2 复合索引顺序(黄金规则)
-- ✅ 等值字段在前,范围字段在后
CREATE INDEX idx ON orders (status, created_at);
-- 支持: WHERE status = 'pending' AND created_at > '2024-01-01'
-- ❌ 反了: 范围在前,等值在后,索引失效
CREATE INDEX idx ON orders (created_at, status);
2.3 索引维护
-- 查未使用索引
SELECT schemaname, tablename, indexname, idx_scan
FROM pg_stat_user_indexes
WHERE idx_scan = 0
AND indexrelname NOT LIKE '%_pkey';
-- 查重复索引
SELECT a.indexname, b.indexname
FROM pg_indexes a, pg_indexes b
WHERE a.tablename = b.tablename
AND a.indexname != b.indexname
AND a.indexdef = b.indexdef;
-- 重建索引(少用,可能锁表)
REINDEX INDEX CONCURRENTLY idx_name;
三、数据类型选型
3.1 推荐类型速查
| 用途 | 推荐 | 避免 | 原因 |
|---|---|---|---|
| IDs(数字) | bigint | int, UUID 字符串 | 性能 5x |
| IDs(分布式) | uuid (v4/v7) | text | 唯一性、空间效率 |
| 时间戳 | timestamptz | timestamp | 带时区、跨区域无歧义 |
| 日期 | date | text / varchar | 节省空间、可索引 |
| 金额 | numeric(10,2) | float, double | 避免精度丢失 |
| 短文本(< 100 字符) | text + CHECK | varchar(n) | PG 没有性能差异 |
| 枚举 | enum 类型 | varchar | 类型安全、节省空间 |
| 布尔 | boolean | int(0/1) | 可读性、约束 |
| JSON | jsonb | json | 可索引、压缩 |
| IP 地址 | inet | text | 可比较、节省空间 |
| URL | text | varchar(2048) | URL 长度不固定 |
3.2 真实案例:避免金钱计算错误
-- ❌ BAD: float 累积误差
ALTER TABLE orders ADD COLUMN amount float;
-- 0.1 + 0.2 = 0.30000000000000004
-- ✅ GOOD: numeric
ALTER TABLE orders ADD COLUMN amount numeric(10,2);
-- 0.1 + 0.2 = 0.30 精确
3.3 真实案例:timestamptz 避免时区 bug
-- ❌ BAD: timestamp,跨时区混乱
CREATE TABLE events (created_at timestamp);
-- 美国用户写:2026-01-15 10:00:00
-- 中国用户读:2026-01-15 10:00:00 (但实际是 23:00 当地时间)
-- ✅ GOOD: timestamptz,存 UTC
CREATE TABLE events (created_at timestamptz DEFAULT now());
-- 美国用户写:2026-01-15 10:00:00-05
-- 中国用户读:2026-01-15 23:00:00+08 (自动转换)
四、反模式检测 SQL
4.1 检测未索引外键
-- 查所有没加索引的外键
SELECT
conrelid::regclass AS table_name,
a.attname AS column_name
FROM pg_constraint c
JOIN pg_attribute a
ON a.attrelid = c.conrelid
AND a.attnum = ANY(c.conkey)
WHERE c.contype = 'f'
AND NOT EXISTS (
SELECT 1
FROM pg_index i
WHERE i.indrelid = c.conrelid
AND a.attnum = ANY(i.indkey)
);
4.2 查慢查询
-- 需要先启用 pg_stat_statements
CREATE EXTENSION IF NOT EXISTS pg_stat_statements;
SELECT pg_reload_conf();
-- Top 10 慢查询
SELECT
substring(query, 1, 100) AS query_preview,
calls,
round(mean_exec_time::numeric, 2) AS avg_ms,
round(total_exec_time::numeric, 2) AS total_ms
FROM pg_stat_statements
WHERE mean_exec_time > 100
ORDER BY mean_exec_time DESC
LIMIT 10;
4.3 查表膨胀
-- 表膨胀(死元组过多)
SELECT
schemaname || '.' || relname AS table_name,
n_live_tup,
n_dead_tup,
round(100.0 * n_dead_tup / NULLIF(n_live_tup + n_dead_tup, 0), 2) AS dead_pct,
last_vacuum,
last_autovacuum
FROM pg_stat_user_tables
WHERE n_dead_tup > 1000
ORDER BY n_dead_tup DESC;
4.4 查缺失索引
-- 顺序扫描次数多的表(可能缺索引)
SELECT
schemaname || '.' || relname AS table_name,
seq_scan,
seq_tup_read,
idx_scan,
round(100.0 * seq_scan / NULLIF(seq_scan + idx_scan, 0), 2) AS seq_pct
FROM pg_stat_user_tables
WHERE seq_scan > 1000
ORDER BY seq_scan DESC;
4.5 查未使用索引
-- 几乎没被使用的索引(浪费)
SELECT
schemaname || '.' || indexrelname AS index_name,
idx_scan,
pg_size_pretty(pg_relation_size(indexrelid)) AS size
FROM pg_stat_user_indexes
WHERE idx_scan < 50
ORDER BY pg_relation_size(indexrelid) DESC;
五、Supabase 安全配置
5.1 安全默认值
-- 撤销 public schema 权限
REVOKE ALL ON SCHEMA public FROM public;
GRANT USAGE ON SCHEMA public TO authenticated;
-- 撤销默认权限
ALTER DEFAULT PRIVILEGES IN SCHEMA public
REVOKE EXECUTE ON FUNCTIONS FROM PUBLIC;
5.2 RLS(Row Level Security)
-- 启用 RLS
ALTER TABLE user_posts ENABLE ROW LEVEL SECURITY;
-- 用户只能看自己的
CREATE POLICY "Users can view own posts"
ON user_posts
FOR SELECT
USING (auth.uid() = user_id);
-- 用户只能改自己的
CREATE POLICY "Users can update own posts"
ON user_posts
FOR UPDATE
USING (auth.uid() = user_id)
WITH CHECK (auth.uid() = user_id);
-- 公开帖子所有人都能看
CREATE POLICY "Public posts are viewable by everyone"
ON user_posts
FOR SELECT
USING (is_public = true);
5.3 性能配置
-- 限制最大连接数
ALTER SYSTEM SET max_connections = 100;
-- 每个查询的内存
ALTER SYSTEM SET work_mem = '8MB';
-- 事务超时
ALTER SYSTEM SET idle_in_transaction_session_timeout = '30s';
-- 防止长查询
ALTER SYSTEM SET statement_timeout = '30s';
-- 重启或 reload
SELECT pg_reload_conf();
5.4 启用监控
-- pg_stat_statements(必备)
CREATE EXTENSION IF NOT EXISTS pg_stat_statements;
SELECT pg_reload_conf();
-- pgstattuple(膨胀分析)
CREATE EXTENSION IF NOT EXISTS pgstattuple;
-- pg_trgm(模糊搜索)
CREATE EXTENSION IF NOT EXISTS pg_trgm;
六、高效分页与 UPSERT
6.1 游标分页(推荐)
-- ❌ BAD: OFFSET 慢
SELECT * FROM products
ORDER BY id
LIMIT 20 OFFSET 10000;
-- 扫描前 10020 行,扔掉前 10000
-- ✅ GOOD: 游标分页
SELECT * FROM products
WHERE id > $last_id
ORDER BY id
LIMIT 20;
-- 直接定位,无扫描浪费
6.2 UPSERT(INSERT or UPDATE)
INSERT INTO settings (user_id, key, value)
VALUES (123, 'theme', 'dark')
ON CONFLICT (user_id, key)
DO UPDATE SET
value = EXCLUDED.value,
updated_at = now();
6.3 批量 UPSERT
INSERT INTO products (sku, name, price)
VALUES
('sku1', 'Product 1', 9.99),
('sku2', 'Product 2', 19.99),
('sku3', 'Product 3', 29.99)
ON CONFLICT (sku)
DO UPDATE SET
name = EXCLUDED.name,
price = EXCLUDED.price;
七、真实踩坑案例
案例 1:char vs varchar 误解
现象:用 char(10) 存用户名,查询不带空格出错。
根因:char(n) 总是补足到 n 个空格。
解决:用 text 或 varchar,PG 没性能差异。
案例 2:UUID v4 做主键性能差
现象:B-tree 索引膨胀,写入慢。 根因:UUID v4 完全随机,导致 B-tree 频繁分裂。 解决:用 UUID v7(带时间戳前缀),或 bigint + sequence。
案例 3:JSONB 字段没索引
现象:WHERE payload->>'country' = 'US' 全表扫描。
根因:JSONB 字段默认无索引。
解决:建 GIN 索引 CREATE INDEX i ON t USING gin (payload).
案例 4:RLS 性能灾难
现象:开了 RLS 后查询慢 10 倍。
根因:RLS 是 row-by-row 检查;复杂的 USING 表达式未走索引。
解决:用 (select auth.uid()) 包一层,让 planner 物化。
-- ❌ BAD: 每次 row 都调用 auth.uid()
USING (auth.uid() = user_id)
-- ✅ GOOD: 物化
USING ((select auth.uid()) = user_id)
案例 5:vacuum 没跑导致表膨胀
现象:磁盘满,查询慢。
根因:autovacuum 没启用或被禁用。
解决:检查 autovacuum 设置,手动 VACUUM FULL(锁表)或 VACUUM(不锁)。
案例 6:连接数爆掉
现象:FATAL: too many clients already。
根因:max_connections 设置过大或连接池未用。
解决:用 PgBouncer 限制连接数,应用层用连接池。
案例 7:full text search 慢
现象:LIKE '%keyword%' 跑了 30 秒。
根因:LIKE 前缀模糊不走索引。
解决:用 tsvector + GIN 索引。
ALTER TABLE articles ADD COLUMN search_vector tsvector
GENERATED ALWAYS AS (to_tsvector('english', title || ' ' || body)) STORED;
CREATE INDEX idx_articles_search ON articles USING gin (search_vector);
-- 查询
SELECT * FROM articles
WHERE search_vector @@ to_tsquery('english', 'postgres & performance');
案例 8:timestamp 范围查询走错索引
现象:WHERE created_at BETWEEN '2026-01-01' AND '2026-01-31' 慢。
根因:复合索引顺序错了(status, created_at,但 WHERE 没有 status)。
解决:加 CREATE INDEX i ON t (created_at),或用 partial index 优化。
案例 9:null 处理 bug
现象:WHERE deleted_at IS NULL 没走索引。
根因:NULL 在 PG 索引中是特殊值。
解决:用 partial index WHERE deleted_at IS NULL,或改用 deleted boolean 字段。
案例 10:money 类型陷阱
现象:用 money 类型,跨币种出错。
根因:money 是单币种固定类型。
解决:用 numeric(20, 4) + 单独 currency 字段。
八、性能基准
8.1 索引 vs 全表扫描(100 万行)
| 场景 | 无索引 | B-tree 索引 | GIN 索引 |
|---|---|---|---|
| 等值查询 | 800ms | 5ms | - |
| 范围查询 | 800ms | 15ms | - |
| JSONB 包含 | 1200ms | - | 20ms |
| 数组 contains | 1500ms | - | 30ms |
| 全文搜索 | 3000ms | - | 50ms |
8.2 OFFSET vs 游标分页(10 亿行)
| 页 | OFFSET 时间 | 游标时间 |
|---|---|---|
| 第 1 页 | 5ms | 5ms |
| 第 1000 页 | 250ms | 5ms |
| 第 10 万页 | 30s+ | 5ms |
结论:深度分页必须用游标。
8.3 UPSERT vs INSERT+UPDATE
| 场景 | 单独 INSERT+UPDATE | UPSERT |
|---|---|---|
| 100 万行 | 60s | 15s |
| 冲突率 1% | 90s | 20s |
| 冲突率 50% | 180s | 35s |
九、与其他 Skills 配合
| Skill | 配合 |
|---|---|
| clickhouse-io | OLAP 数据同步到 ClickHouse |
| backend-patterns | API 层数据库访问最佳实践 |
| database-reviewer (Agent) | 全库审查,调用本 Skill |
| supabase-admin (若有) | Supabase 专属管理 |
| verification-loop | 数据库迁移后验证 |
完整工作流:
database-reviewer (全库扫描)
↓
postgres-patterns (匹配规则)
↓
输出报告 (按严重度)
↓
人工确认 + 修复
↓
verification-loop (验证)
十、5 条反合理化
| 借口 | 反驳 |
|---|---|
| ”ORM 自动 SQL 没事” | ORM 生成的 SQL 经常有反模式 |
| ”索引越多越快” | 索引拖累写入,浪费空间 |
| ”text 和 varchar 一样” | 但 varchar(n) 仍可能产生性能问题 |
| ”OFFSET 简单够用” | 深度分页灾难 |
| ”RLS 性能损耗大” | 正确写法(物化 auth.uid())损耗 < 5% |
十一、5 条实战技巧
- 生产前必跑反模式检测 SQL:把上面的 4 条 SQL 集成到 CI
- 启用 pg_stat_statements + auto_explain:自动捕获慢查询
- 所有外键必加索引:写 migration 时强制
- 数字主键用 bigint:避免 2038 年问题
- 金额一律 numeric:不存为 float
十二、Q&A
Q: 必须订阅 Claude Code 吗? A: Skill 在 Claude Sonnet/Opus 上效果最佳,其他 LLM 也可借鉴。
Q: 和 backend-patterns 区别? A: postgres-patterns 聚焦 SQL 和数据库本身;backend-patterns 聚焦 API 层。
Q: 跟 clickhouse-io 关系? A: Postgres = OLTP(事务),ClickHouse = OLAP(分析),可组合使用。
Q: 跟 Supabase CLI 关系? A: Skill 提供”知识”,Supabase CLI 提供”工具”(migration、db push)。
Q: 老的 schema 改造适用吗? A: 适合,配合 database-reviewer Agent 渐进式改造。
Q: 中文支持? A: SQL 关键字英文,注释和字段名可中文。
Q: 学习曲线? A: 中等。需懂 SQL 基础和 PG 概念。
十三、安装
# Claude Code
/plugin marketplace add affaan-m/everything-claude-code
/plugin install everything-claude-code@everything-claude-code
cp -r everything-claude-code/rules/common ~/.claude/rules/
# 通用
npx skills add affaan-m/everything-claude-code --skill postgres-patterns
十四、总结
核心价值:
- PostgreSQL 模式速查 + 反模式检测 SQL
- Supabase RLS 安全配置
- 索引策略、数据类型、高效分页
- 真实踩坑案例
适用人群:
- 后端开发者(Node + Supabase / Rails / Django / Spring)
- 数据工程师
- Supabase 项目
- AI 辅助生成 SQL 场景
投入产出比:⭐⭐⭐⭐⭐(5/5)—— 所有 PG 项目必装。
何时不要用:
- OLAP(用 clickhouse-io)
- NoSQL(MongoDB / Redis 专属)
- 嵌入式(SQLite 够用)
- 没用过 PG(先学基础)
配套文档:clickhouse-io 分析 | backend-patterns 后端 | database-reviewer Agent
参考资料
快速安装
git clone https://github.com/affaan-m/everything-claude-code.git
cd everything-claude-code
ls skills/postgres-patterns