为什么数据库设计适合用 AI
数据库设计是一个既需要经验又需要细心的工作。表结构设计、索引选择、查询优化——每一步都有大量的最佳实践和常见陷阱。
AI 在这个领域特别有用,因为:
- 它见过海量的数据库设计案例
- 它能快速生成规范的 DDL 语句
- 它能分析 SQL 查询的性能问题
- 它能帮你考虑到容易遗漏的边界情况
我们来看一套完整的数据库设计提示词模板。
提示词 1:需求到 ER 图
你是一个数据库架构师。请根据以下业务需求设计 ER 图。
业务描述:
[业务描述]
请输出:
1. 实体列表(每个实体的属性和主键)
2. 实体之间的关系(一对一、一对多、多对多)
3. 用文本格式画出 ER 图
4. 设计决策的说明(为什么这样设计)
注意:
- 考虑数据的完整性约束
- 标注哪些字段可以为 NULL
- 标注唯一约束
使用示例
业务描述:
我们要做一个在线课程平台。用户可以注册成为学生或讲师。
讲师可以创建课程,每个课程有多个章节。
学生可以购买课程,购买后可以观看所有章节。
学生可以对课程评分和评论。
每个课程有标签,方便搜索。
AI 输出:
实体列表:
1. users(用户)
- id: BIGINT, PK, AUTO_INCREMENT
- email: VARCHAR(255), UNIQUE, NOT NULL
- password_hash: VARCHAR(255), NOT NULL
- name: VARCHAR(100), NOT NULL
- role: ENUM('student', 'instructor'), NOT NULL
- avatar_url: VARCHAR(500), NULL
- created_at: TIMESTAMP, NOT NULL, DEFAULT NOW()
2. courses(课程)
- id: BIGINT, PK, AUTO_INCREMENT
- instructor_id: BIGINT, FK -> users.id, NOT NULL
- title: VARCHAR(200), NOT NULL
- description: TEXT, NULL
- price: DECIMAL(10,2), NOT NULL
- status: ENUM('draft', 'published', 'archived'), DEFAULT 'draft'
- created_at: TIMESTAMP, NOT NULL
3. chapters(章节)
- id: BIGINT, PK
- course_id: BIGINT, FK -> courses.id, NOT NULL
- title: VARCHAR(200), NOT NULL
- video_url: VARCHAR(500), NOT NULL
- duration_seconds: INT, NOT NULL
- sort_order: INT, NOT NULL
4. enrollments(选课记录)
- id: BIGINT, PK
- user_id: BIGINT, FK -> users.id, NOT NULL
- course_id: BIGINT, FK -> courses.id, NOT NULL
- purchased_at: TIMESTAMP, NOT NULL
- UNIQUE(user_id, course_id)
5. reviews(评价)
- id: BIGINT, PK
- user_id: BIGINT, FK -> users.id, NOT NULL
- course_id: BIGINT, FK -> courses.id, NOT NULL
- rating: SMALLINT, NOT NULL, CHECK(1-5)
- comment: TEXT, NULL
- created_at: TIMESTAMP, NOT NULL
- UNIQUE(user_id, course_id)
6. tags(标签)
- id: BIGINT, PK
- name: VARCHAR(50), UNIQUE, NOT NULL
7. course_tags(课程标签关联)
- course_id: BIGINT, FK -> courses.id
- tag_id: BIGINT, FK -> tags.id
- PRIMARY KEY(course_id, tag_id)
关系:
users 1:N courses(一个讲师多个课程)
courses 1:N chapters(一个课程多个章节)
users M:N courses(通过 enrollments)
users M:N courses(通过 reviews)
courses M:N tags(通过 course_tags)
提示词 2:ER 图到 DDL
请将以下 ER 图转换为 [数据库类型] 的 DDL 语句。
ER 图:
[ER 图描述]
要求:
1. 包含所有表的 CREATE TABLE 语句
2. 包含外键约束
3. 包含索引(基于常见查询场景)
4. 包含注释
5. 按照依赖顺序排列(被引用的表先创建)
6. 数据库类型:[PostgreSQL/MySQL/SQLite]
输出示例
-- 用户表
CREATE TABLE users (
id BIGSERIAL PRIMARY KEY,
email VARCHAR(255) NOT NULL UNIQUE,
password_hash VARCHAR(255) NOT NULL,
name VARCHAR(100) NOT NULL,
role VARCHAR(20) NOT NULL CHECK (role IN ('student', 'instructor')),
avatar_url VARCHAR(500),
created_at TIMESTAMP NOT NULL DEFAULT NOW()
);
COMMENT ON TABLE users IS '用户表,包含学生和讲师';
COMMENT ON COLUMN users.role IS '用户角色:student 或 instructor';
-- 课程表
CREATE TABLE courses (
id BIGSERIAL PRIMARY KEY,
instructor_id BIGINT NOT NULL REFERENCES users(id),
title VARCHAR(200) NOT NULL,
description TEXT,
price DECIMAL(10,2) NOT NULL CHECK (price >= 0),
status VARCHAR(20) NOT NULL DEFAULT 'draft'
CHECK (status IN ('draft', 'published', 'archived')),
created_at TIMESTAMP NOT NULL DEFAULT NOW()
);
CREATE INDEX idx_courses_instructor ON courses(instructor_id);
CREATE INDEX idx_courses_status ON courses(status);
-- 章节表
CREATE TABLE chapters (
id BIGSERIAL PRIMARY KEY,
course_id BIGINT NOT NULL REFERENCES courses(id) ON DELETE CASCADE,
title VARCHAR(200) NOT NULL,
video_url VARCHAR(500) NOT NULL,
duration_seconds INT NOT NULL CHECK (duration_seconds > 0),
sort_order INT NOT NULL,
UNIQUE(course_id, sort_order)
);
CREATE INDEX idx_chapters_course ON chapters(course_id);
-- 选课记录表
CREATE TABLE enrollments (
id BIGSERIAL PRIMARY KEY,
user_id BIGINT NOT NULL REFERENCES users(id),
course_id BIGINT NOT NULL REFERENCES courses(id),
purchased_at TIMESTAMP NOT NULL DEFAULT NOW(),
UNIQUE(user_id, course_id)
);
CREATE INDEX idx_enrollments_user ON enrollments(user_id);
CREATE INDEX idx_enrollments_course ON enrollments(course_id);
-- 评价表
CREATE TABLE reviews (
id BIGSERIAL PRIMARY KEY,
user_id BIGINT NOT NULL REFERENCES users(id),
course_id BIGINT NOT NULL REFERENCES courses(id),
rating SMALLINT NOT NULL CHECK (rating BETWEEN 1 AND 5),
comment TEXT,
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
UNIQUE(user_id, course_id)
);
CREATE INDEX idx_reviews_course ON reviews(course_id);
提示词 3:业务查询生成
你是一个 SQL 专家。请根据以下表结构和业务需求,编写 SQL 查询。
表结构:
[DDL 或表结构描述]
查询需求:
[用自然语言描述你想查什么]
要求:
1. 使用 [PostgreSQL/MySQL] 语法
2. 查询要高效(避免不必要的子查询和全表扫描)
3. 添加注释说明查询逻辑
4. 如果需要索引支持,请指出
常见查询场景模板
场景 1:排行榜查询
"查询评分最高的前 10 门课程,包含课程名、讲师名、平均评分、评价数量"
场景 2:统计报表
"按月统计每个讲师的课程销售额和学生数量"
场景 3:复杂筛选
"查询价格在 100-500 之间、评分 4 分以上、包含 'Python' 标签的课程"
场景 4:用户行为分析
"查询购买了课程 A 的用户中,有多少也购买了课程 B(协同过滤)"
提示词 4:查询优化
请优化以下 SQL 查询。
原始查询:
```sql
[你的 SQL]
执行计划(如果有): [EXPLAIN 输出]
当前问题: [描述性能问题,如”查询耗时 5 秒”]
表数据量:
- [表名]:约 [数量] 行
- [表名]:约 [数量] 行
请:
- 分析性能瓶颈
- 给出优化后的 SQL
- 建议需要添加的索引
- 估算优化后的性能提升
### 优化示例
原始查询(慢): SELECT c.*, AVG(r.rating) as avg_rating FROM courses c LEFT JOIN reviews r ON c.id = r.course_id WHERE c.status = ‘published’ GROUP BY c.id HAVING AVG(r.rating) > 4 ORDER BY avg_rating DESC LIMIT 10;
表数据量:courses 10万行,reviews 100万行
AI 优化建议:
问题分析:
- LEFT JOIN 100万行的 reviews 表,然后 GROUP BY,开销很大
- 没有利用索引
优化方案:
-- 方案 1:使用子查询预聚合
SELECT
c.*,
rs.avg_rating
FROM courses c
INNER JOIN (
SELECT
course_id,
AVG(rating) as avg_rating
FROM reviews
GROUP BY course_id
HAVING AVG(rating) > 4
) rs ON c.id = rs.course_id
WHERE c.status = 'published'
ORDER BY rs.avg_rating DESC
LIMIT 10;
-- 建议索引
CREATE INDEX idx_reviews_course_rating ON reviews(course_id, rating);
CREATE INDEX idx_courses_status ON courses(status);
预估提升:从 5 秒降到 0.2 秒以内。
---
## 提示词 5:迁移脚本生成
请生成数据库迁移脚本。
当前表结构: [当前 DDL]
目标变更: [描述要做的变更]
要求:
- 生成 UP 迁移(升级)和 DOWN 迁移(回滚)
- 考虑数据迁移(如果有)
- 考虑在线迁移的安全性(不锁表或最小化锁表时间)
- 添加注释说明每步操作
- 使用 [Alembic/Flyway/Prisma] 格式
### 使用示例
目标变更:
- 给 courses 表添加 category_id 字段
- 创建 categories 表
- 将现有课程的标签迁移为分类
AI 输出:
— UP 迁移 — Step 1: 创建分类表 CREATE TABLE categories ( id BIGSERIAL PRIMARY KEY, name VARCHAR(100) NOT NULL UNIQUE, parent_id BIGINT REFERENCES categories(id), sort_order INT NOT NULL DEFAULT 0, created_at TIMESTAMP NOT NULL DEFAULT NOW() );
— Step 2: 添加 category_id 字段(允许 NULL,因为现有数据还没有分类) ALTER TABLE courses ADD COLUMN category_id BIGINT REFERENCES categories(id);
— Step 3: 数据迁移 - 从标签创建分类 INSERT INTO categories (name) SELECT DISTINCT t.name FROM tags t INNER JOIN course_tags ct ON t.id = ct.tag_id;
— Step 4: 数据迁移 - 为课程设置分类(取第一个标签作为分类) UPDATE courses c SET category_id = ( SELECT cat.id FROM categories cat INNER JOIN tags t ON t.name = cat.name INNER JOIN course_tags ct ON ct.tag_id = t.id AND ct.course_id = c.id LIMIT 1 );
— Step 5: 添加索引 CREATE INDEX idx_courses_category ON courses(category_id);
— DOWN 迁移 DROP INDEX IF EXISTS idx_courses_category; ALTER TABLE courses DROP COLUMN IF EXISTS category_id; DROP TABLE IF EXISTS categories;
---
## 提示词 6:索引推荐
请分析以下表结构和查询模式,推荐索引策略。
表结构: [DDL]
常见查询模式:
- [查询描述 + SQL]
- [查询描述 + SQL]
- [查询描述 + SQL]
写入模式:
- 每秒约 [N] 次插入
- 每秒约 [N] 次更新
请输出:
- 推荐的索引列表
- 每个索引的作用(加速哪些查询)
- 索引的存储开销估算
- 不建议添加的索引及原因
---
## 提示词 7:数据库选型
请帮我做数据库选型。
业务场景: [描述业务场景]
数据特征:
- 数据量:[预估]
- 读写比:[如 读:写 = 10:1]
- 数据结构:[结构化/半结构化/非结构化]
- 一致性要求:[强一致/最终一致]
- 查询模式:[OLTP/OLAP/混合]
约束条件:
- 团队技术栈:[现有经验]
- 预算:[预算范围]
- 运维能力:[自建/托管]
请对比分析 2-3 种候选数据库,给出推荐。
### 输出示例
场景:实时数据分析平台,每天 1 亿条日志,需要秒级查询
| 维度 | PostgreSQL | ClickHouse | Elasticsearch |
|---|---|---|---|
| 写入性能 | 中 | 高 | 高 |
| 查询性能 | 中 | 很高 | 高 |
| 聚合分析 | 好 | 很好 | 一般 |
| 全文搜索 | 一般 | 差 | 很好 |
| 运维复杂度 | 低 | 中 | 高 |
| 团队学习成本 | 低 | 中 | 中 |
推荐:ClickHouse 原因:日志分析场景,写入量大,聚合查询为主,ClickHouse 的列式存储和向量化执行引擎最适合。
---
## 提示词 8:数据建模审查
请审查以下数据库设计,指出问题和改进建议。
表结构: [DDL]
业务场景: [描述]
请从以下维度审查:
- 范式化程度(是否过度范式化或反范式化)
- 数据完整性(约束是否完整)
- 性能考虑(索引、分区)
- 扩展性(未来需求变化时是否容易修改)
- 命名规范
---
## 组合使用:完整的数据库设计流程
把上面的提示词串联起来,形成一个完整的工作流:
第 1 步:需求 → ER 图(提示词 1) ↓ 第 2 步:ER 图 → DDL(提示词 2) ↓ 第 3 步:DDL 审查(提示词 8) ↓ 第 4 步:编写业务查询(提示词 3) ↓ 第 5 步:查询优化(提示词 4) ↓ 第 6 步:索引推荐(提示词 6) ↓ 第 7 步:生成迁移脚本(提示词 5)
每一步都用专门的提示词,前一步的输出作为后一步的输入。这就是 Prompt Chaining 在数据库设计中的实际应用。
---
## 使用技巧
### 1. 提供足够的上下文
AI 不知道你的业务,所以要把业务场景描述清楚。"用户表"可以有一百种设计,但"日活 10 万的 SaaS 平台的用户表"就具体多了。
### 2. 指定数据库类型
PostgreSQL 和 MySQL 的语法差异不小。始终明确指定数据库类型。
### 3. 给出数据量级
"优化这个查询"和"优化这个查询,表有 1 亿行"是完全不同的问题。
### 4. 验证生成的 SQL
AI 生成的 SQL 不一定能直接运行。始终在测试环境验证。
> 数据库设计是软件的地基。用 AI 加速设计过程,但最终的决策权在你手里。AI 是你的参谋,不是你的替代。 相关文章
评论
加载中...
评论
加载中...