database-schema-design by secondsky/claude-skills
npx skills add https://github.com/secondsky/claude-skills --skill database-schema-design适用于 PostgreSQL 和 MySQL 的全面数据库模式设计模式,包含规范化、关系、约束和错误预防。
步骤 1:从模板中选择您的模式模式:
# 包含用户、产品、订单的基础模式
cat templates/basic-schema.sql
# 关系模式(1:1, 1:M, M:M)
cat templates/relationships.sql
# 约束示例
cat templates/constraints.sql
# 审计模式
cat templates/audit-columns.sql
步骤 2:应用规范化规则(至少达到 3NF):
references/normalization-guide.md 获取详细示例步骤 3:为每个表添加必要元素:
CREATE TABLE your_table (
-- 主键(必需)
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
-- 具有适当类型的业务列
name VARCHAR(200) NOT NULL, -- 使用合适的长度
-- 审计列(始终包含)
created_at TIMESTAMPTZ DEFAULT NOW() NOT NULL,
updated_at TIMESTAMPTZ DEFAULT NOW() NOT NULL
);
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
| 规则 | 原因 |
|---|---|
| 每个表都有主键 | 确保行唯一性,启用关系 |
| 明确定义外键 | 强制引用完整性,防止孤立记录 |
| 为所有外键建立索引 | 防止慢速 JOIN,对性能至关重要 |
| 必需字段使用 NOT NULL | 数据完整性,防止 NULL 污染 |
| 审计列(created_at, updated_at) | 跟踪变更、调试、合规性 |
| 合适的数据类型 | 存储效率、验证、索引 |
| 为枚举使用检查约束 | 在数据库级别强制执行有效值 |
| 指定 ON DELETE/UPDATE 规则 | 防止意外数据丢失或孤立记录 |
| 反模式 | 为何不好 |
|---|---|
| 到处使用 VARCHAR(MAX) | 浪费空间,减慢索引,无验证 |
| 日期存储为 VARCHAR | 无法进行日期计算,无验证,排序失效 |
| 缺少外键 | 无引用完整性,产生孤立记录 |
| 过早反规范化 | 难以维护,数据异常 |
| EAV(实体-属性-值) | 查询复杂,无类型安全,速度慢 |
| 多态关联 | 无外键完整性,查询复杂 |
| 循环依赖 | 无法填充数据,破坏级联操作 |
| 外键无索引 | JOIN 操作极慢,性能杀手 |
症状:无法唯一标识行,数据重复 修复方法:
-- ❌ 错误做法
CREATE TABLE users (
email VARCHAR(255),
name VARCHAR(100)
);
-- ✅ 正确做法
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
email VARCHAR(255) UNIQUE NOT NULL,
name VARCHAR(100) NOT NULL
);
症状:孤立记录,数据不一致 修复方法:
-- ❌ 错误做法
CREATE TABLE orders (
id UUID PRIMARY KEY,
user_id UUID -- 无约束!
);
-- ✅ 正确做法
CREATE TABLE orders (
id UUID PRIMARY KEY,
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE
);
-- 为外键建立索引
CREATE INDEX idx_orders_user_id ON orders(user_id);
症状:浪费空间,索引速度慢,无验证 修复方法:
-- ❌ 错误做法
CREATE TABLE products (
name VARCHAR(MAX),
sku VARCHAR(MAX),
status VARCHAR(MAX)
);
-- ✅ 正确做法
CREATE TABLE products (
name VARCHAR(200) NOT NULL,
sku VARCHAR(50) UNIQUE NOT NULL,
status VARCHAR(20) NOT NULL
CHECK (status IN ('draft', 'active', 'archived'))
);
症状:无日期验证,排序失效,无法进行日期计算 修复方法:
-- ❌ 错误做法
CREATE TABLE events (
event_date VARCHAR(50) -- '2025-12-15' 还是 'Dec 15, 2025'?
);
-- ✅ 正确做法
CREATE TABLE events (
event_date DATE NOT NULL, -- 已验证,可排序
event_time TIMESTAMPTZ -- 带时区
);
症状:JOIN 操作极慢,查询性能差 修复方法:
-- 始终为外键建立索引
CREATE TABLE order_items (
order_id UUID NOT NULL REFERENCES orders(id),
product_id UUID NOT NULL REFERENCES products(id)
);
-- ✅ 必需的索引
CREATE INDEX idx_order_items_order_id ON order_items(order_id);
CREATE INDEX idx_order_items_product_id ON order_items(product_id);
症状:无法跟踪记录创建/修改时间 修复方法:
-- ❌ 错误做法
CREATE TABLE products (
id UUID PRIMARY KEY,
name VARCHAR(200)
);
-- ✅ 正确做法
CREATE TABLE products (
id UUID PRIMARY KEY,
name VARCHAR(200) NOT NULL,
created_at TIMESTAMPTZ DEFAULT NOW() NOT NULL,
updated_at TIMESTAMPTZ DEFAULT NOW() NOT NULL
);
-- 自动更新触发器(PostgreSQL)
CREATE TRIGGER products_updated_at
BEFORE UPDATE ON products
FOR EACH ROW
EXECUTE FUNCTION update_updated_at_column();
症状:查询复杂,无类型安全,性能慢 修复方法:
-- ❌ 错误做法(EAV)
CREATE TABLE product_attributes (
product_id UUID,
attribute_name VARCHAR(100), -- 'color', 'size', 'price'
attribute_value TEXT -- 所有内容都存储为文本!
);
-- ✅ 正确做法(结构化 + JSONB)
CREATE TABLE products (
id UUID PRIMARY KEY,
name VARCHAR(200) NOT NULL,
price DECIMAL(10,2) NOT NULL, -- 必需字段作为列
color VARCHAR(50), -- 常见属性作为列
size VARCHAR(20),
attributes JSONB -- 可选/动态属性
);
-- 为 JSONB 建立索引
CREATE INDEX idx_products_attributes ON products USING GIN(attributes);
加载 references/error-catalog.md 获取所有 12 个错误的详细修复方法。
| 模式 | 使用场景 | 模板 |
|---|---|---|
| 基础 CRUD | 标准用户/产品/订单 | templates/basic-schema.sql |
| 一对一 | 用户 → 个人资料 | templates/relationships.sql(第 7-17 行) |
| 一对多 | 用户 → 订单 | templates/relationships.sql(第 23-34 行) |
| 多对多 | 学生 ↔ 课程 | templates/relationships.sql(第 40-60 行) |
| 层次结构 | 类别树,组织架构图 | templates/relationships.sql(第 66-83 行) |
| 软删除 | 标记删除,保留历史 | templates/audit-columns.sql(第 55-80 行) |
| 版本控制 | 随时间跟踪变更 | templates/audit-columns.sql(第 86-108 行) |
| 多租户 | 每个组织的数据隔离 | references/schema-design-patterns.md(第 228-258 行) |
| 范式 | 规则 | 示例 |
|---|---|---|
| 1NF | 原子值,无重复组 | phone1, phone2 → phones 表 |
| 2NF | 1NF + 无部分依赖 | 复合键依赖 → 单独的表 |
| 3NF | 2NF + 无传递依赖 | user.city → city.id 引用 |
| BCNF | 3NF + 每个决定因素都是候选键 | 罕见边缘情况 |
| 4NF | BCNF + 无多值依赖 | 复杂多对多 |
| 5NF | 4NF + 无连接依赖 | 非常罕见,学术性质 |
建议:设计到 3NF,仅在测量性能数据后进行反规范化。
加载 references/normalization-guide.md 获取包含前后对比的详细示例。
-- 主键
id UUID PRIMARY KEY DEFAULT gen_random_uuid()
-- 或针对性能关键场景:
id BIGSERIAL PRIMARY KEY
-- 文本
name VARCHAR(200) NOT NULL
description TEXT
code CHAR(10) -- 仅用于固定长度代码
-- 数字
price DECIMAL(10,2) NOT NULL -- 货币:绝不要使用 FLOAT
quantity INT NOT NULL
rating DECIMAL(3,2) -- 0.00 到 9.99
-- 日期/时间
created_at TIMESTAMPTZ DEFAULT NOW() NOT NULL -- 带时区
event_date DATE
duration INTERVAL
-- 布尔值
is_active BOOLEAN DEFAULT true NOT NULL
-- JSON
attributes JSONB -- 二进制,更快,可索引
-- 枚举替代方案(优于 ENUM 类型)
status VARCHAR(20) NOT NULL
CHECK (status IN ('draft', 'active', 'archived'))
-- MySQL 没有:
TIMESTAMPTZ -- 使用 TIMESTAMP(以 UTC 存储)
gen_random_uuid() -- 使用 UUID() 函数
JSONB -- 使用 JSON(8.0+ 版本性能相同)
-- MySQL 等效方案:
id CHAR(36) PRIMARY KEY DEFAULT (UUID())
-- 或:
id BIGINT AUTO_INCREMENT PRIMARY KEY
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL
attributes JSON
加载 references/data-types-guide.md 获取全面的类型选择指南。
加载 references/schema-design-patterns.md 当:
加载 references/normalization-guide.md 当:
加载 references/relationship-patterns.md 当:
加载 references/data-types-guide.md 当:
加载 references/constraints-catalog.md 当:
加载 references/error-catalog.md 当:
创建表之前:
每个表必须包含:
外键:
索引:
验证:
之前(存在多个问题):
CREATE TABLE users (
email VARCHAR(MAX), -- 问题:无主键,VARCHAR(MAX)
password VARCHAR(MAX),
created VARCHAR(50) -- 问题:日期存储为字符串
);
CREATE TABLE orders (
id UUID PRIMARY KEY,
user_email VARCHAR(MAX), -- 问题:无外键
total VARCHAR(20), -- 问题:货币存储为字符串
status VARCHAR(MAX) -- 问题:无验证
);
之后(生产就绪):
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
email VARCHAR(255) UNIQUE NOT NULL,
password_hash VARCHAR(255) NOT NULL,
created_at TIMESTAMPTZ DEFAULT NOW() NOT NULL,
updated_at TIMESTAMPTZ DEFAULT NOW() NOT NULL
);
CREATE TABLE orders (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
total DECIMAL(10,2) NOT NULL CHECK (total >= 0),
status VARCHAR(20) NOT NULL DEFAULT 'pending'
CHECK (status IN ('pending', 'processing', 'shipped', 'delivered', 'canceled')),
created_at TIMESTAMPTZ DEFAULT NOW() NOT NULL,
updated_at TIMESTAMPTZ DEFAULT NOW() NOT NULL
);
CREATE INDEX idx_orders_user_id ON orders(user_id);
CREATE INDEX idx_orders_status ON orders(status);
结果:✅ 所有约束强制执行,类型正确,已索引,可审计
所有 12 个文档化错误均已预防:
参见:references/error-catalog.md 获取详细修复方法
模板:
templates/basic-schema.sql - 用户、产品、订单入门模板templates/relationships.sql - 所有关系类型templates/constraints.sql - 约束示例templates/audit-columns.sql - 审计模式 + 触发器参考文档:
references/normalization-guide.md - 1NF 到 5NF 详细说明references/relationship-patterns.md - 关系类型references/data-types-guide.md - PostgreSQL 与 MySQL 类型对比references/constraints-catalog.md - 所有约束references/schema-design-patterns.md - 最佳实践references/error-catalog.md - 所有 12 个错误文档官方文档:
生产环境测试 | 预防 12 个错误 | MIT 许可证
每周安装次数
105
代码仓库
GitHub 星标数
90
首次出现
2026年1月24日
安全审计
已安装于
opencode89
gemini-cli88
codex88
cursor86
github-copilot83
kimi-cli81
Comprehensive database schema design patterns for PostgreSQL and MySQL with normalization, relationships, constraints, and error prevention.
Step 1 : Choose your schema pattern from templates:
# Basic schema with users, products, orders
cat templates/basic-schema.sql
# Relationship patterns (1:1, 1:M, M:M)
cat templates/relationships.sql
# Constraint examples
cat templates/constraints.sql
# Audit patterns
cat templates/audit-columns.sql
Step 2 : Apply normalization rules (at minimum 3NF):
references/normalization-guide.md for detailed examplesStep 3 : Add essential elements to every table:
CREATE TABLE your_table (
-- Primary key (required)
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
-- Business columns with proper types
name VARCHAR(200) NOT NULL, -- Use appropriate lengths
-- Audit columns (always include)
created_at TIMESTAMPTZ DEFAULT NOW() NOT NULL,
updated_at TIMESTAMPTZ DEFAULT NOW() NOT NULL
);
| Rule | Reason |
|---|---|
| Every table has PRIMARY KEY | Ensures row uniqueness, enables relationships |
| Foreign keys defined explicitly | Enforces referential integrity, prevents orphans |
| Index all foreign keys | Prevents slow JOINs, critical for performance |
| NOT NULL on required fields | Data integrity, prevents NULL pollution |
| Audit columns (created_at, updated_at) | Track changes, debugging, compliance |
| Appropriate data types | Storage efficiency, validation, indexing |
| Check constraints for enums | Enforces valid values at database level |
| ON DELETE/UPDATE rules specified | Prevents accidental data loss or orphans |
| Anti-Pattern | Why It's Bad |
|---|---|
| VARCHAR(MAX) everywhere | Wastes space, slows indexes, no validation |
| Dates as VARCHAR | No date math, no validation, sorting broken |
| Missing foreign keys | No referential integrity, orphaned records |
| Premature denormalization | Hard to maintain, data anomalies |
| EAV (Entity-Attribute-Value) | Query complexity, no type safety, slow |
| Polymorphic associations | No foreign key integrity, complex queries |
| Circular dependencies | Impossible to populate, breaks CASCADE |
| No indexes on foreign keys | Extremely slow JOINs, performance killer |
Symptom : Cannot uniquely identify rows, duplicate data Fix :
-- ❌ Bad
CREATE TABLE users (
email VARCHAR(255),
name VARCHAR(100)
);
-- ✅ Good
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
email VARCHAR(255) UNIQUE NOT NULL,
name VARCHAR(100) NOT NULL
);
Symptom : Orphaned records, data inconsistency Fix :
-- ❌ Bad
CREATE TABLE orders (
id UUID PRIMARY KEY,
user_id UUID -- No constraint!
);
-- ✅ Good
CREATE TABLE orders (
id UUID PRIMARY KEY,
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE
);
-- Index the foreign key
CREATE INDEX idx_orders_user_id ON orders(user_id);
Symptom : Wasted space, slow indexes, no validation Fix :
-- ❌ Bad
CREATE TABLE products (
name VARCHAR(MAX),
sku VARCHAR(MAX),
status VARCHAR(MAX)
);
-- ✅ Good
CREATE TABLE products (
name VARCHAR(200) NOT NULL,
sku VARCHAR(50) UNIQUE NOT NULL,
status VARCHAR(20) NOT NULL
CHECK (status IN ('draft', 'active', 'archived'))
);
Symptom : No date validation, broken sorting, no date math Fix :
-- ❌ Bad
CREATE TABLE events (
event_date VARCHAR(50) -- '2025-12-15' or 'Dec 15, 2025'?
);
-- ✅ Good
CREATE TABLE events (
event_date DATE NOT NULL, -- Validated, sortable
event_time TIMESTAMPTZ -- With timezone
);
Symptom : Extremely slow JOINs, poor query performance Fix :
-- Always index foreign keys
CREATE TABLE order_items (
order_id UUID NOT NULL REFERENCES orders(id),
product_id UUID NOT NULL REFERENCES products(id)
);
-- ✅ Required indexes
CREATE INDEX idx_order_items_order_id ON order_items(order_id);
CREATE INDEX idx_order_items_product_id ON order_items(product_id);
Symptom : Cannot track when records created/modified Fix :
-- ❌ Bad
CREATE TABLE products (
id UUID PRIMARY KEY,
name VARCHAR(200)
);
-- ✅ Good
CREATE TABLE products (
id UUID PRIMARY KEY,
name VARCHAR(200) NOT NULL,
created_at TIMESTAMPTZ DEFAULT NOW() NOT NULL,
updated_at TIMESTAMPTZ DEFAULT NOW() NOT NULL
);
-- Auto-update trigger (PostgreSQL)
CREATE TRIGGER products_updated_at
BEFORE UPDATE ON products
FOR EACH ROW
EXECUTE FUNCTION update_updated_at_column();
Symptom : Complex queries, no type safety, slow performance Fix :
-- ❌ Bad (EAV)
CREATE TABLE product_attributes (
product_id UUID,
attribute_name VARCHAR(100), -- 'color', 'size', 'price'
attribute_value TEXT -- Everything as text!
);
-- ✅ Good (Structured + JSONB)
CREATE TABLE products (
id UUID PRIMARY KEY,
name VARCHAR(200) NOT NULL,
price DECIMAL(10,2) NOT NULL, -- Required fields as columns
color VARCHAR(50), -- Common attributes as columns
size VARCHAR(20),
attributes JSONB -- Optional/dynamic attributes
);
-- Index JSONB
CREATE INDEX idx_products_attributes ON products USING GIN(attributes);
Load references/error-catalog.md for all 12 errors with detailed fixes.
| Pattern | Use Case | Template |
|---|---|---|
| Basic CRUD | Standard users/products/orders | templates/basic-schema.sql |
| One-to-One | User → Profile | templates/relationships.sql (lines 7-17) |
| One-to-Many | User → Orders | templates/relationships.sql (lines 23-34) |
| Many-to-Many | Students ↔ Courses | templates/relationships.sql (lines 40-60) |
| Form | Rule | Example |
|---|---|---|
| 1NF | Atomic values, no repeating groups | phone1, phone2 → phones table |
| 2NF | 1NF + no partial dependencies | Composite key dependency → separate table |
| 3NF | 2NF + no transitive dependencies | user.city → city.id reference |
| BCNF | 3NF + every determinant is candidate key | Rare edge cases |
| 4NF |
Recommendation : Design to 3NF, denormalize only with measured performance data.
Load references/normalization-guide.md for detailed examples with before/after.
-- Primary Keys
id UUID PRIMARY KEY DEFAULT gen_random_uuid()
-- OR for performance-critical:
id BIGSERIAL PRIMARY KEY
-- Text
name VARCHAR(200) NOT NULL
description TEXT
code CHAR(10) -- Fixed-length codes only
-- Numbers
price DECIMAL(10,2) NOT NULL -- Money: NEVER use FLOAT
quantity INT NOT NULL
rating DECIMAL(3,2) -- 0.00 to 9.99
-- Dates/Times
created_at TIMESTAMPTZ DEFAULT NOW() NOT NULL -- With timezone
event_date DATE
duration INTERVAL
-- Boolean
is_active BOOLEAN DEFAULT true NOT NULL
-- JSON
attributes JSONB -- Binary, faster, indexable
-- Enum Alternative (preferred over ENUM type)
status VARCHAR(20) NOT NULL
CHECK (status IN ('draft', 'active', 'archived'))
-- MySQL doesn't have:
TIMESTAMPTZ -- Use TIMESTAMP (stored as UTC)
gen_random_uuid() -- Use UUID() function
JSONB -- Use JSON (same performance in 8.0+)
-- MySQL equivalent:
id CHAR(36) PRIMARY KEY DEFAULT (UUID())
-- OR:
id BIGINT AUTO_INCREMENT PRIMARY KEY
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL
attributes JSON
Load references/data-types-guide.md for comprehensive type selection guide.
Load references/schema-design-patterns.md when:
Load references/normalization-guide.md when:
Load references/relationship-patterns.md when:
Load references/data-types-guide.md when:
Load references/constraints-catalog.md when:
Load references/error-catalog.md when:
Before Creating Tables :
Every Table Must Have :
Foreign Keys :
Indexes :
Validation :
Before (Multiple issues):
CREATE TABLE users (
email VARCHAR(MAX), -- Issue: No primary key, VARCHAR(MAX)
password VARCHAR(MAX),
created VARCHAR(50) -- Issue: Date as string
);
CREATE TABLE orders (
id UUID PRIMARY KEY,
user_email VARCHAR(MAX), -- Issue: No foreign key
total VARCHAR(20), -- Issue: Money as string
status VARCHAR(MAX) -- Issue: No validation
);
After (Production-ready):
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
email VARCHAR(255) UNIQUE NOT NULL,
password_hash VARCHAR(255) NOT NULL,
created_at TIMESTAMPTZ DEFAULT NOW() NOT NULL,
updated_at TIMESTAMPTZ DEFAULT NOW() NOT NULL
);
CREATE TABLE orders (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
total DECIMAL(10,2) NOT NULL CHECK (total >= 0),
status VARCHAR(20) NOT NULL DEFAULT 'pending'
CHECK (status IN ('pending', 'processing', 'shipped', 'delivered', 'canceled')),
created_at TIMESTAMPTZ DEFAULT NOW() NOT NULL,
updated_at TIMESTAMPTZ DEFAULT NOW() NOT NULL
);
CREATE INDEX idx_orders_user_id ON orders(user_id);
CREATE INDEX idx_orders_status ON orders(status);
Result : ✅ All constraints enforced, proper types, indexed, auditable
All 12 documented errors prevented:
See : references/error-catalog.md for detailed fixes
Templates :
templates/basic-schema.sql - Users, products, orders startertemplates/relationships.sql - All relationship typestemplates/constraints.sql - Constraint examplestemplates/audit-columns.sql - Audit patterns + triggersReferences :
references/normalization-guide.md - 1NF through 5NF detailedreferences/relationship-patterns.md - Relationship typesreferences/data-types-guide.md - PostgreSQL vs MySQL typesreferences/constraints-catalog.md - All constraintsreferences/schema-design-patterns.md - Best practicesreferences/error-catalog.md - All 12 errors documentedOfficial Documentation :
Production-tested | 12 errors prevented | MIT License
Weekly Installs
105
Repository
GitHub Stars
90
First Seen
Jan 24, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
opencode89
gemini-cli88
codex88
cursor86
github-copilot83
kimi-cli81
React 组合模式指南:Vercel 组件架构最佳实践,提升代码可维护性
116,600 周安装
Shopify Polaris Web Components 使用指南:为 App Home 构建 UI 的完整教程
104 周安装
每日新闻摘要生成器 - AI自动汇总多源新闻,智能生成Markdown报告
104 周安装
Obsidian CLI 官方命令行工具使用指南:文件管理、搜索、属性与任务操作
104 周安装
流程图创建器 - 在线生成交互式HTML流程图、决策树和工作流可视化工具
104 周安装
send-file 技能:Telegram 文件发送工具,支持图片、文档、音频、视频一键发送
104 周安装
约定式提交规范指南:自动化版本管理与变更日志生成
104 周安装
| Categories tree, org chart |
templates/relationships.sql (lines 66-83) |
| Soft Delete | Mark deleted, keep history | templates/audit-columns.sql (lines 55-80) |
| Versioning | Track changes over time | templates/audit-columns.sql (lines 86-108) |
| Multi-Tenant | Isolated data per organization | references/schema-design-patterns.md (lines 228-258) |
| BCNF + no multi-valued dependencies |
| Complex many-to-many |
| 5NF | 4NF + no join dependencies | Very rare, academic |