mcpField
D
database-schema-designer

database-schema-designer

github.com/softaworks数据与数据库2.6k4.3k 次安装34 周前更新
代码仓库

关于 database-schema-designer

database-schema-designer 根据领域需求和访问模式设计 SQL 或 NoSQL 数据库结构,支持生成与审查结构、索引规划、规范化及可回滚迁移。

主要功能

  1. 01生成 SQL 和 NoSQL 结构
  2. 02建模实体与关系
  3. 03应用规范化与反规范化
  4. 04规划索引和约束
  5. 05创建可回滚迁移
  6. 06审查现有数据库结构

适用场景

  1. 01设计电商平台的数据表
  2. 02根据访问模式选择数据模型
  3. 03通过索引改善查询性能
  4. 04规划可回滚的结构变更

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/softaworks/agent-toolkit --skill database-schema-designer

安装后在 Claude Code 中输入/database-schema-designer确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

README.md
assets/templates/
migration-template.sql
references/
schema-design-checklist.md

详细介绍

数据库 Schema 设计器

设计生产级数据库 Schema,内置最佳实践。


快速开始

只需描述你的数据模型:

design a schema for an e-commerce platform with users, products, orders

你将获得一份完整的 SQL Schema,例如:

CREATE TABLE users (
  id BIGINT AUTO_INCREMENT PRIMARY KEY,
  email VARCHAR(255) UNIQUE NOT NULL,
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE orders (
  id BIGINT AUTO_INCREMENT PRIMARY KEY,
  user_id BIGINT NOT NULL REFERENCES users(id),
  total DECIMAL(10,2) NOT NULL,
  INDEX idx_orders_user (user_id)
);

请求中应包含的内容:

  • 实体(用户、产品、订单)
  • 关键关系(用户拥有订单,订单包含商品)
  • 规模提示(高流量、数百万条记录)
  • 数据库偏好(SQL/NoSQL)—— 未指定时默认使用 SQL

触发词

触发词示例
design schema"design a schema for user authentication"
database design"database design for multi-tenant SaaS"
create tables"create tables for a blog system"
schema for"schema for inventory management"
model data"model data for real-time analytics"
I need a database"I need a database for tracking orders"
design NoSQL"design NoSQL schema for product catalog"

关键术语

术语定义
规范化(Normalization)组织数据以减少冗余(1NF → 2NF → 3NF)
3NF第三范式 —— 列之间不存在传递依赖
OLTP在线事务处理 —— 写密集,需要规范化
OLAP在线分析处理 —— 读密集,受益于反规范化
外键(FK)引用另一张表主键的列
索引加速查询的数据结构(代价是写入变慢)
访问模式应用读写数据的方式(查询、连接、过滤)
反规范化有意复制数据以加速读取

快速参考

任务方法关键考虑
新 Schema先规范化至 3NF领域建模优先于 UI
SQL vs NoSQL由访问模式决定读写比例很重要
主键INT 或 UUID分布式系统使用 UUID
外键始终添加约束ON DELETE 策略至关重要
索引外键 + WHERE 列列顺序很重要
迁移始终可逆优先向后兼容

流程概览

Your Data Requirements
    |
    v
+-----------------------------------------------------+
| Phase 1: ANALYSIS                                   |
| * Identify entities and relationships               |
| * Determine access patterns (read vs write heavy)   |
| * Choose SQL or NoSQL based on requirements         |
+-----------------------------------------------------+
    |
    v
+-----------------------------------------------------+
| Phase 2: DESIGN                                     |
| * Normalize to 3NF (SQL) or embed/reference (NoSQL) |
| * Define primary keys and foreign keys              |
| * Choose appropriate data types                     |
| * Add constraints (UNIQUE, CHECK, NOT NULL)         |
+-----------------------------------------------------+
    |
    v
+-----------------------------------------------------+
| Phase 3: OPTIMIZE                                   |
| * Plan indexing strategy                            |
| * Consider denormalization for read-heavy queries   |
| * Add timestamps (created_at, updated_at)           |
+-----------------------------------------------------+
    |
    v
+-----------------------------------------------------+
| Phase 4: MIGRATE                                    |
| * Generate migration scripts (up + down)            |
| * Ensure backward compatibility                     |
| * Plan zero-downtime deployment                     |
+-----------------------------------------------------+
    |
    v
Production-Ready Schema

命令

命令使用时机操作
design schema for {domain}从零开始完整的 Schema 生成
normalize {table}修复现有表应用规范化规则
add indexes for {table}性能问题生成索引策略
migration for {change}Schema 演进创建可逆迁移
review schema代码审查审查现有 Schema

工作流: 从 design schema 开始 → 用 normalize 迭代 → 用 add indexes 优化 → 用 migration 演进


核心原则

原则原因实现方式
对领域建模UI 会变,领域不会实体名称反映业务概念
数据完整性优先数据损坏的修复成本高昂在数据库层面添加约束
针对访问模式优化无法同时为两者优化OLTP:规范化,OLAP:反规范化
为规模做规划事后改造非常痛苦索引策略 + 分区方案

反模式

避免原因替代方案
到处使用 VARCHAR(255)浪费存储空间,掩盖意图按字段合理设定长度
用 FLOAT 存储金额舍入误差DECIMAL(10,2)
缺少外键约束产生孤立数据始终定义外键
外键上没有索引JOIN 变慢为每个外键建立索引
将日期存储为字符串无法比较/排序DATE、TIMESTAMP 类型
查询中使用 SELECT *获取不必要的数据明确列出所需列
不可逆的迁移无法回滚始终编写 DOWN 迁移
添加 NOT NULL 但不设默认值破坏现有行先添加可空列,回填数据,再加约束

验证清单

设计完 schema 后:

  • 每张表都有主键
  • 所有关系都有外键约束
  • 为每个 FK 定义了 ON DELETE 策略
  • 所有外键上都有索引
  • 高频查询列上都有索引
  • 合适的数据类型(金额用 DECIMAL 等)
  • 必填字段设置 NOT NULL
  • 需要处设置 UNIQUE 约束
  • 用于校验的 CHECK 约束
  • created_at 和 updated_at 时间戳
  • 迁移脚本可回滚
  • 已在 staging 上用生产数据测试

深入探讨:范式化(SQL)
范式
范式规则违规示例
1NF原子值,无重复组product_ids = '1,2,3'
2NF1NF + 无部分依赖order_items 中的 customer_name
3NF2NF + 无传递依赖由 postal_code 推导出的 country
第一范式(1NF)
-- BAD: Multiple values in column
CREATE TABLE orders (
  id INT PRIMARY KEY,
  product_ids VARCHAR(255)  -- '101,102,103'
);

-- GOOD: Separate table for items
CREATE TABLE orders (
  id INT PRIMARY KEY,
  customer_id INT
);

CREATE TABLE order_items (
  id INT PRIMARY KEY,
  order_id INT REFERENCES orders(id),
  product_id INT
);
第二范式(2NF)
-- BAD: customer_name depends only on customer_id
CREATE TABLE order_items (
  order_id INT,
  product_id INT,
  customer_name VARCHAR(100),  -- Partial dependency!
  PRIMARY KEY (order_id, product_id)
);

-- GOOD: Customer data in separate table
CREATE TABLE customers (
  id INT PRIMARY KEY,
  name VARCHAR(100)
);
第三范式(3NF)
-- BAD: country depends on postal_code
CREATE TABLE customers (
  id INT PRIMARY KEY,
  postal_code VARCHAR(10),
  country VARCHAR(50)  -- Transitive dependency!
);

-- GOOD: Separate postal_codes table
CREATE TABLE postal_codes (
  code VARCHAR(10) PRIMARY KEY,
  country VARCHAR(50)
);
何时反范式化
场景反范式化策略
读密集的报表预计算的聚合值
昂贵的 JOIN缓存的派生列
分析仪表盘物化视图
-- Denormalized for performance
CREATE TABLE orders (
  id INT PRIMARY KEY,
  customer_id INT,
  total_amount DECIMAL(10,2),  -- Calculated
  item_count INT               -- Calculated
);
深入探讨:数据类型
字符串类型
类型使用场景示例
CHAR(n)固定长度州代码、ISO 日期
VARCHAR(n)可变长度姓名、邮箱
TEXT长内容文章、描述
-- Good sizing
email VARCHAR(255)
phone VARCHAR(20)
country_code CHAR(2)
数值类型
类型范围使用场景
TINYINT-128 到 127年龄、状态码
SMALLINT-32K 到 32K数量
INT-2.1B 到 2.1BID、计数
BIGINT非常大大 ID、时间戳
DECIMAL(p,s)精确精度金额
FLOAT/DOUBLE近似科学数据
-- ALWAYS use DECIMAL for money
price DECIMAL(10, 2)  -- $99,999,999.99

-- NEVER use FLOAT for money
price FLOAT  -- Rounding errors!
日期/时间类型
DATE        -- 2025-10-31
TIME        -- 14:30:00
DATETIME    -- 2025-10-31 14:30:00
TIMESTAMP   -- Auto timezone conversion

-- Always store in UTC
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
布尔值
-- PostgreSQL
is_active BOOLEAN DEFAULT TRUE

-- MySQL
is_active TINYINT(1) DEFAULT 1
深入探讨:索引策略
何时创建索引
始终建索引原因
外键加速 JOIN
WHERE 子句列加速过滤
ORDER BY 列加速排序
唯一约束强制唯一性
-- Foreign key index
CREATE INDEX idx_orders_customer ON orders(customer_id);

-- Query pattern index
CREATE INDEX idx_orders_status_date ON orders(status, created_at);
索引类型
类型最适合示例
B-Tree范围、等值price > 100
Hash仅精确匹配email = '[email protected]'
全文文本搜索MATCH AGAINST
部分行的子集WHERE is_active = true
复合索引顺序
CREATE INDEX idx_customer_status ON orders(customer_id, status);

-- Uses index (customer_id first)
SELECT * FROM orders WHERE customer_id = 123;
SELECT * FROM orders WHERE customer_id = 123 AND status = 'pending';

-- Does NOT use index (status alone)
SELECT * FROM orders WHERE status = 'pending';

规则: 选择性最高的列在前,或单独查询最多的列在前。

索引陷阱
陷阱问题解决方案
过度建索引写入变慢只为被查询的列建索引
列顺序错误索引未被使用匹配查询模式
缺少 FK 索引JOIN 变慢始终为 FK 建索引
深入探讨:约束
主键
-- Auto-increment (simple)
id INT AUTO_INCREMENT PRIMARY KEY

-- UUID (distributed systems)
id CHAR(36) PRIMARY KEY DEFAULT (UUID())

-- Composite (junction tables)
PRIMARY KEY (student_id, course_id)
外键
FOREIGN KEY (customer_id) REFERENCES customers(id)
  ON DELETE CASCADE     -- Delete children with parent
  ON DELETE RESTRICT    -- Prevent deletion if referenced
  ON DELETE SET NULL    -- Set to NULL when parent deleted
  ON UPDATE CASCADE     -- Update children when parent changes
策略何时使用
CASCADE有依赖的数据(order_items)
RESTRICT重要引用(防止误操作)
SET NULL可选关系
其他约束
-- Unique
email VARCHAR(255) UNIQUE NOT NULL

-- Composite unique
UNIQUE (student_id, course_id)

-- Check
price DECIMAL(10,2) CHECK (price >= 0)
discount INT CHECK (discount BETWEEN 0 AND 100)

-- Not null
name VARCHAR(100) NOT NULL
深入探讨:关系模式
一对多
CREATE TABLE orders (
  id INT PRIMARY KEY,
  customer_id INT NOT NULL REFERENCES customers(id)
);

CREATE TABLE order_items (
  id INT PRIMARY KEY,
  order_id INT NOT NULL REFERENCES orders(id) ON DELETE CASCADE,
  product_id INT NOT NULL,
  quantity INT NOT NULL
);
多对多
-- Junction table
CREATE TABLE enrollments (
  student_id INT REFERENCES students(id) ON DELETE CASCADE,
  course_id INT REFERENCES courses(id) ON DELETE CASCADE,
  enrolled_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (student_id, course_id)
);
自引用
CREATE TABLE employees (
  id INT PRIMARY KEY,
  name VARCHAR(100) NOT NULL,
  manager_id INT REFERENCES employees(id)
);
多态
-- Approach 1: Separate FKs (stronger integrity)
CREATE TABLE comments (
  id INT PRIMARY KEY,
  content TEXT NOT NULL,
  post_id INT REFERENCES posts(id),
  photo_id INT REFERENCES photos(id),
  CHECK (
    (post_id IS NOT NULL AND photo_id IS NULL) OR
    (post_id IS NULL AND photo_id IS NOT NULL)
  )
);

-- Approach 2: Type + ID (flexible, weaker integrity)
CREATE TABLE comments (
  id INT PRIMARY KEY,
  content TEXT NOT NULL,
  commentable_type VARCHAR(50) NOT NULL,
  commentable_id INT NOT NULL
);
深入探讨:NoSQL 设计(MongoDB)
嵌入 vs 引用
因素嵌入引用
访问模式一起读取分别读取
关系1:少1:多
文档大小小接近 16MB
更新频率很少频繁
嵌入式文档
{
  "_id": "order_123",
  "customer": {
    "id": "cust_456",
    "name": "Jane Smith",
    "email": "[email protected]"
  },
  "items": [
    { "product_id": "prod_789", "quantity": 2, "price": 29.99 }
  ],
  "total": 109.97
}
引用文档
{
  "_id": "order_123",
  "customer_id": "cust_456",
  "item_ids": ["item_1", "item_2"],
  "total": 109.97
}
MongoDB 索引
// Single field
db.users.createIndex({ email: 1 }, { unique: true });

// Composite
db.orders.createIndex({ customer_id: 1, created_at: -1 });

// Text search
db.articles.createIndex({ title: "text", content: "text" });

// Geospatial
db.stores.createIndex({ location: "2dsphere" });
深入探讨:迁移
迁移最佳实践
实践原因
始终可逆需要回滚
向后兼容零停机部署
先 Schema 后数据关注点分离
在预发布环境测试及早发现问题
添加列(零停机)
-- Step 1: Add nullable column
ALTER TABLE users ADD COLUMN phone VARCHAR(20);

-- Step 2: Deploy code that writes to new column

-- Step 3: Backfill existing rows
UPDATE users SET phone = '' WHERE phone IS NULL;

-- Step 4: Make required (if needed)
ALTER TABLE users MODIFY phone VARCHAR(20) NOT NULL;
重命名列(零停机)
-- Step 1: Add new column
ALTER TABLE users ADD COLUMN email_address VARCHAR(255);

-- Step 2: Copy data
UPDATE users SET email_address = email;

-- Step 3: Deploy code reading from new column
-- Step 4: Deploy code writing to new column

-- Step 5: Drop old column
ALTER TABLE users DROP COLUMN email;
迁移模板
-- Migration: YYYYMMDDHHMMSS_description.sql

-- UP
BEGIN;
ALTER TABLE users ADD COLUMN phone VARCHAR(20);
CREATE INDEX idx_users_phone ON users(phone);
COMMIT;

-- DOWN
BEGIN;
DROP INDEX idx_users_phone ON users;
ALTER TABLE users DROP COLUMN phone;
COMMIT;
深入探讨:性能优化
查询分析
EXPLAIN SELECT * FROM orders
WHERE customer_id = 123 AND status = 'pending';
关注项含义
type: ALL全表扫描(差)
type: ref使用了索引(好)
key: NULL未使用索引
rows: 很高扫描了大量行
N+1 查询问题
# BAD: N+1 queries
orders = db.query("SELECT * FROM orders")
for order in orders:
    customer = db.query(f"SELECT * FROM customers WHERE id = {order.customer_id}")

# GOOD: Single JOIN
results = db.query("""
    SELECT orders.*, customers.name
    FROM orders
    JOIN customers ON orders.customer_id = customers.id
""")
优化技巧
技巧使用时机
添加索引WHERE/ORDER BY 缓慢
反规范化JOIN 开销大
分页结果集很大
缓存重复查询
只读副本读负载高
分区表非常大

扩展点

  1. 数据库特定模式: 添加 MySQL vs PostgreSQL vs SQLite 的差异
  2. 高级模式: 时间序列、事件溯源、CQRS、多租户
  3. ORM 集成: TypeORM、Prisma、SQLAlchemy 模式
  4. 监控: 查询性能跟踪、慢查询告警

评论

支持 Markdown