数据库
需要为开发/演示/测试数据库生成真实…
当已有 Drizzle schema、SQL migrations、Raw SQL 或 Prisma schema,需要生成可重复运行的 TypeScript/SQL 种子数据时使用。
🧪 尚未被验证(还没有小呱复用过的记录,不代表不好)
GDI C 38/100
进化评分(GDI)
综合排序依据 —— 四个维度的加权结果,让你看出「为什么这条排在前面」
内在质量 55/100 ×0.35
使用度 0/100 ×0.30
口碑 50/100 ×0.20
新鲜度 60/100 ×0.15
适用场景
需要为开发/演示/测试数据库生成真实、幂特定
解决思路
- 扫描项目中的 Drizzle schema 定义:`src/db/schema.ts`、`src/schema/*.ts`、`db/schema.ts`。
- 扫描项目中的 D1 migrations:`drizzle/*.sql`、`migrations/*.sql`。
- 扫描项目中的 Raw SQL:`schema.sql`、`db/*.sql`。
- 扫描项目中的 Prisma:`prisma/schema.prisma`。
- 读取所有 schema 文件,建立对表的理解:表及其列。
- 记录数据类型和约束(NOT NULL、UNIQUE、DEFAULT)。
- 记录外键关系:哪些表引用哪些表。
- 识别以 TEXT 存储的 JSON 字段(常见于 D1/SQLite)。
- 询问用户种子用途(Purpose):dev、demo、testing,默认 dev。
- 询问用户数据量(Volume):small(5-10 rows/table)、medium(20-50)、large(100+),默认 small。
- 询问用户领域上下文(Domain context):如 'e-commerce store'、'SaaS app'、'blog' 等,默认从 schema 推断。
- 询问用户输出格式(Output format):TypeScript (Drizzle)、raw SQL 或 both,默认匹配项目 ORM。
- 当用途为 dev 时,使用多变数据并包含一些边界情况(空字段、长字符串、unicode)。
- 当用途为 demo 时,使用精修、适合截图和演示的数据。
- 当用途为 testing 时,使用系统性数据覆盖边界条件、重复值、特殊字符。
- 根据外键构建依赖图,先插入父表再插入子表。
- 博客 schema 示例插入顺序:1 users(无依赖);2 categories(无依赖);3 posts(依赖 users、categories);4 comments(依赖 users、posts);5 tags(无依赖);6 post_tags(依赖 posts、tags)。
- 遇到循环依赖时,使用可空外键并分两遍插入:先插入 NULL,再 UPDATE。
- 不要使用通用占位符如 'test123'、'foo@bar.com'、'Lorem ipsum';生成与领域匹配的数据。
- 生成数据无需外部库。
- 生成姓名:使用硬编码的常见名字列表,混合性别和文化背景,例如 `const firstNames = ['Sarah', 'James', 'Priya', 'Mohammed', 'Emma', 'Wei', 'Carlos', 'Aisha']; const lastNames = ['Chen', 'Smith', 'Patel', 'Garcia', 'Kim', 'O'Brien', 'Nguyen', 'Wilson'];`。
- 生成邮箱:从姓名派生,如 `sarah.chen@example.com`;使用 `example.com` 域名(RFC 2606 保留)。
- 生成日期:在真实范围内生成;D1/SQLite 使用 ISO 8601 格式;可用 `const randomDate = (daysBack: number) => { const d = new Date(); d.setDate(d.getDate() - Math.floor(Math.random() * daysBack)); return d.toISOString(); };`。
- 生成 ID:UUID 使用 `crypto.randomUUID()`;如果 schema 使用自增则用连续整数。
- 需要可复现数据时使用确定性种子:seeded PRNG,例如 `function seededRandom(seed: number) { return () => { seed = (seed * 16807) % 2147483647; return (seed - 1) / 2147483646; }; } const rand = seededRandom(42);`,相同 seed 生成相同数据。
- 生成价格/金额:使用真实范围,例如 `(rand() * 900 + 100).toFixed(2)` 对应 $1-$10 范围。
- 生成描述/内容:每种内容类型写 3-5 个真实变体并循环使用;不要生成 AI 腔调文本,要像真实用户数据。
- 输出 TypeScript(Drizzle ORM)到 `scripts/seed.ts`,导入 `drizzle-orm/d1` 和 `../src/db/schema`。
- 在 TypeScript 中定义 `export async function seed(db: ReturnType<typeof drizzle>)`。
- 在 seed 函数开始时执行 `console.log('Seeding database...')`。
- 按依赖反向顺序清空已有数据,例如 `await db.delete(schema.comments); await db.delete(schema.posts); await db.delete(schema.users);`。
- 插入 users 数组,每条包含 `id: crypto.randomUUID()`、`name`、`email` 等字段。
- 处理 D1 batch limit:每 10 行执行一次 INSERT,例如 `for (let i = 0; i < users.length; i += 10) { await db.insert(schema.users).values(users.slice(i, i + 10)); }`。
- 插入 posts 时引用已插入 users 的 ID,并同样每 10 行分批插入。
- 最后打印种子数量,例如 `console.log(`Seeded: ${users.length} users, ${posts.length} posts`);`。
- TypeScript seed 运行命令:`npx tsx scripts/seed.ts`。
- 对 Cloudflare Workers,添加 seed endpoint(生产前移除):`app.post('/api/seed', async (c) => { const db = drizzle(c.env.DB); await seed(db); return c.json({ ok: true }); });`。
- 输出 Raw SQL(D1)到 `seed.sql`。
- 在 Raw SQL 文件顶部注释运行命令:`npx wrangler d1 execute DB_NAME --local --file=./scripts/seed.sql`。
- 在 Raw SQL 中按反向依赖顺序 DELETE:`DELETE FROM comments; DELETE FROM posts; DELETE FROM users;`。
- Raw SQL 用户插入示例:`INSERT INTO users (id, name, email, created_at) VALUES ('uuid-1', 'Sarah Chen', 'sarah@example.com', '2025-01-15T10:30:00Z'), ('uuid-2', 'James Wilson', 'james@example.com', '2025-02-01T14:22:00Z');`。
- Raw SQL posts 插入时引用真实 `user_id`,D1 每个 INSERT 最多 10 行。
- 实现幂等 Option A:delete-then-insert(简单,会丢失数据):`await db.delete(schema.users); await db.insert(schema.users).values(seedUsers);`。
- 实现幂等 Option B:upsert(保留非种子数据):`for (const user of seedUsers) { await db.insert(schema.users).values(user).onConflictDoUpdate({ target: schema.users.id, set: user }); }`。
- 默认 dev/testing 使用 Option A,demo 使用 Option B。
- D1 gotcha:每个 INSERT 最多约 10 行,按 10 行分块批量插入。
- D1 gotcha:无原生 BOOLEAN,使用 INTEGER(0/1)。
- D1 gotcha:无原生 DATETIME,使用 TEXT 和 ISO 8601 字符串。
- D1 gotcha:JSON 存储为 TEXT,插入前调用 `JSON.stringify()`。
- D1 gotcha:外键总是强制,先插入父表。
- D1 gotcha:100 个绑定参数限制,保持 batch size × columns < 100。
- 质量规则 1:匹配领域——电商种子用真实感的产品名和价格,而不是 'Product 1'。
- 质量规则 2:变化数据——不要让每个用户都叫 'John Smith',每个价格都是 '$9.99'。
- 质量规则 3:测试种子包含边界情况——空字符串、非常长文本、特殊字符、最大值。
- 质量规则 4:引用真实 ID——外键必须指向实际已插入的父行。
- 质量规则 5:打印种子内容——总是记录数量以便用户知道成功。
- 质量规则 6:记录运行命令——放在文件顶部注释。
适用边界 · 注意事项
- 没有可读取的 schema 定义(Drizzle schema、D1 migrations、Raw SQL 或 Prisma schema)时,无法确定表、约束和外键,不适用。
- 目标是生产数据库、需要真实个人数据或需要数据库迁移时,不应使用种子脚本,避免污染生产数据或泄露隐私。
- 运行环境不支持 Claude Code 且没有 Read/Write/Edit/Glob/Grep/Bash 等工具时,不适用。