做数据库迁移这活儿,最磨人的不是迁移逻辑本身,而是那几十上百条 SQL 脚本要一条条手写、核对、排错。刚转岗到某个电商项目负责数据侧的时候,光一个订单表拆状态字段的变更,我就在测试库来回折腾了两天。后来被同事安利了 Claude Code,试着让它帮忙写迁移脚本,实测下来,效率提升确实不是一点点。
这篇文章就把我这几个月用 Claude Code 写 SQL 脚本的完整套路整理出来,从信息采集到提示词模板,从生成流程到常见坑点,全是我实际敲过的命令、踩过的雷。适合正在做数据库迁移、或者被一堆 ALTER TABLE 烦到头疼的开发者参考,不管你是刚工作不久的新手,还是已经带过几个项目的老手,里面应该都有能直接抄作业的东西。
1. 我为什么把数据库迁移脚本交给 Claude Code 来写
1.1 迁移 SQL 的重复性与隐蔽错误
数据库迁移脚本有个特点:逻辑不算复杂,但工作量大、细节极多。比如把一个users表里的phone字段拆成country_code和phone_number两个字段,听起来简单,实操中要处理旧数据回填、唯一索引重建、外键引用更新、应用层查询兼容,任何一环漏掉都会埋雷。
更麻烦的是,这类 SQL 的结构高度重复——几十张表做同样的类型变更,每张表写一遍,手写很容易疲劳出错。我见过最典型的错误是:某张表的ALTER TABLE语句里少写了AFTER子句,导致字段顺序和代码里SELECT *的映射全乱了。这种错误靠肉眼 review 很难发现,但执行后影响面很大。
Claude Code 擅长处理的就是这类任务:规律明确、模式重复、需要严格遵循语法规范的代码生成。给它一份现有表结构的上下文,再告诉它目标 Schema,它能按一致性较高的风格批量产出脚本,而且不会像人一样写到后半段就烦躁。
1.2 Claude Code 相比直接提问式的 AI 工具强在哪
用过网页版 AI 助手写 SQL 的朋友应该都有体会:粘贴一段建表语句进去,让它生成 ALTER 还行,但要它结合项目里已有的多张表做联动迁移,就经常答非所问。因为网页版没有项目上下文,你每次都要手动贴大段结构信息,来回复制粘贴本身就在消耗时间。
Claude Code 的差别在于它直接跑在你的项目目录里,可以读取真实的表结构文件、迁移目录、配置文件,甚至能自己翻历史迁移脚本学习这个项目的风格。我在实践中让 Claude Code 直接读取项目的migrations文件夹和schema.sql,它生成的迁移脚本自动遵循了项目已有的命名规范和格式,这个体验是断崖式领先的。
另外,Claude Code 支持多轮迭代修改。我第一次生成的脚本有几处类型映射问题,我直接在对话里说"那张表的外键改成 ON DELETE SET NULL",它就能定位到对应位置并修改,而不是整段推翻了重来。
1.3 适用场景和我划出的边界
用了一段时间后,我总结了 Claude Code 写迁移脚本的边界。
适合交给它的:
- 批量字段类型变更(INT 改 BIGINT、VARCHAR 加长度)
- 根据旧表结构创建新表并迁移数据
- 生成回滚脚本(DOWN 迁移)
- 索引、约束的增删改
- 多表联动的外键关系重建
不建议完全放手给它:
- 涉及敏感数据清洗(比如手机号脱敏、加密字段切换)必须人工逐条审
- 业务逻辑极其特殊的状态机迁移(比如订单状态从单值变多值)建议只让它生成草稿
- 生产环境变更脚本,无论 AI 生成多完美,都必须先在测试库完整跑一遍
这个边界很重要。AI 是放大器,你把清晰的需求喂进去,它给你高效产出;你把模糊的需求喂进去,它给你高效的错误。我的原则是:让 Claude Code 做执行者,我自己做决策者和审阅者。
2. 动手前先做信息采集:一条高质量提示词的前提
2.1 把迁移需求翻译成 AI 能听懂的清单
很多开发者用 AI 写 SQL 效果不好,问题往往出在提示词太笼统。你告诉 Claude Code"帮我把用户表优化一下",它只能给你一份泛泛的建议,因为"优化"这个词可以有一百种理解。
我的做法是先把迁移需求拆成结构化清单,再交给 Claude Code。以字段拆分迁移为例,我的需求清单长这样:
- 表名:
users - 变更内容:将
phone字段拆为country_code(VARCHAR(8))和phone_number(VARCHAR(20)) - 数据迁移规则:
country_code默认填充'+86',phone_number取原phone去掉前 3 位 - 索引保留:原
phone字段上的唯一索引迁移到phone_number字段 - 回滚要求:生成的 DOWN 脚本要把两个新字段合并回
phone
这份清单不一定一次就完美,但至少让 AI 知道你要"做什么",而不是"帮我看看"。我自己的体会是,花十分钟把需求写成清单,比后面花一小时排查 AI 生成的错误脚本要划算得多。
2.2 三种喂给 Claude Code 的信息形态
Claude Code 理解项目上下文的方式有三种,我按实用度排序:
第一种是让它直接读项目里的文件。这是默认方式,效果也最好。在项目目录下启动 Claude Code 后,直接说"读取migrations/001_init.sql和schema.sql,理解现有的表结构",它就能把这些文件内容纳入上下文。比手动复制粘贴省事得多,而且不会截断长文件。
第二种是手动粘贴关键结构。适用于表结构不在当前项目目录、或者你只想让它关注特定几张表的情况。我一般只粘贴相关表的CREATE TABLE语句和对应的CREATE INDEX语句,不粘无关表,减少干扰。
第三种是描述目标 Schema。比如"希望订单表的新结构是:idBIGINT UNSIGNED、order_noVARCHAR(32) UNIQUE...",让它基于描述生成迁移脚本。这种方式最灵活,但也最容易出现偏差,需要后续仔细核对。
2.3 我反复在用的提示词模板
这套模板我调整了几轮才稳定下来,现在基本是复制粘贴再改表名直接用的状态。核心思路是:给角色、给上下文、给需求、给约束、给输出格式。
你现在是资深数据库迁移工程师。项目根目录下有 migrations/ 文件夹和 schema.sql,请先读取这些文件理解现有表结构。 需求: 1. 将 users 表中的 phone 字段拆分为 country_code VARCHAR(8) 和 phone_number VARCHAR(20) 2. country_code 默认值为 '+86' 3. phone_number 取原 phone 从第 4 位开始的内容(去掉前 3 位) 4. 原 phone 字段上的唯一索引迁移到 phone_number 5. 生成 UP 迁移脚本和 DOWN 回滚脚本 约束: - 遵循项目迁移文件的命名规范 - UP 和 DOWN 必须写在同一个迁移文件里 - 新字段追加到原 phone 字段所在位置附近 - 不要修改其他表的结构 - 如果发现需求有歧义,先列出假设并向我确认 输出格式: - 迁移文件名 - UP 脚本 SQL - DOWN 脚本 SQL - 简要说明每一步的意图这套提示词跑下来,生成的脚本基本能直接进测试库。它的核心价值是"约束"部分——如果你不声明"不要修改其他表",AI 有时候会自作聪明帮你把关联表的字段也改了,那个场面我经历过一次,差点把一张统计表的类型整个改掉。
3. 实际生成 SQL 的完整实操流程
3.1 第 1 步:让 Claude Code 读取现有库表结构,生成迁移清单
进入项目目录,启动 Claude Code,我通常用这样的命令开场:
cd /path/to/project claude进去后直接输入指令让它了解现状:
请读取 migrations/ 目录下的所有迁移文件和根目录下的 schema.sql,梳理出所有表名、字段名、字段类型、索引、外键,输出一张表结构清单,按业务模块分组展示。这一步输出什么不重要,重要的是让 Claude Code 把文件内容加载到上下文里。我可以观察它的输出,如果它读文件读漏了,我会再手动补充一句"还有migrations/002_something.sql也读一下"。
生成完表结构清单后,我要求它基于这次迁移目标输出一份变更清单:
基于我接下来的迁移目标——把 users 表的 phone 拆分为 country_code 和 phone_number,列出所有需要变更的点,包括字段变更、索引变更、外键引用影响。这份清单相当于 Claude Code 自己梳理的"施工图"。我只需要五分钟检查一遍,就能发现有没有遗漏的关联表。有一次它列出了三张引用users.phone的外键表,其中一张是我自己都没注意到的历史统计表,这一下就帮我避免了一次上线事故。
3.2 第 2 步:分批次生成变更 SQL,不要一次生成完
这里是我踩过最深的坑,必须强调几次都不嫌多:不要让它一次性生成几十条 SQL。
AI 在长上下文里保持高质量的代价很高。你让它一口气生成 30 张表的变更脚本,前 10 张可能非常规范,越到后面越容易犯低级错误,比如对着一张已经不存在的旧字段做操作。
我的做法是分批生成,每批控制在 5 到 8 条相关变更:
先处理 users 表的字段拆分。请生成该表的 UP 和 DOWN 脚本,严格遵循之前的约束。生成后我审查,确认没问题了,再让它处理下一个表:
users 表没问题。现在处理 orders 表的 status 字段,从 VARCHAR(20) 改为 ENUM('pending','paid','shipped','completed','cancelled'),注意已有数据里可能存在不在枚举值中的状态,请生成清洗方案。分批还有一个好处:方便定位问题。如果第二批生成的脚本有问题,我只需要在那一批范围内排查,不用回头检查整个上下文里哪句话带偏了节奏。
3.3 第 3 步:让 Claude Code 生成回滚脚本
很多开发者写迁移脚本只关注 UP,回滚脚本写得极其敷衍甚至不写。但生产环境的迁移不怕一万就怕万一,没有可靠的 DOWN 脚本,出问题只能手动补数据,那个压力我经历过,属实不想再来一次。
Claude Code 生成回滚脚本的能力相当不错。关键是提示词里要写明回滚的数据映射规则,不能只写"回滚"两个字。
请基于刚才的 UP 脚本生成对应的 DOWN 脚本。回滚规则: 1. 新表字段 country_code 和 phone_number 合并回原 phone 2. 合并规则:country_code 去掉 '+' 号后直接拼接 phone_number(即 '86' + phone_number) 3. 原唯一索引恢复到 phone 字段 4. 外键引用恢复原样这种约束明确的回滚脚本,我测试过几次,执行结果都是可预期的。注意回滚的顺序问题:先处理外键,再删索引,最后合并字段,顺序错了会在执行到一半时爆外键约束错误。
3.4 第 4 步:人工审阅 + 在测试库执行验证
我不管 Claude Code 生成的脚本看起来多完美,一律不在生产环境直接执行。流程是固定的:
先在测试库建一个和当前生产结构一致的副本,然后按顺序执行 UP 脚本,跑完检查数据是否符合预期,再执行 DOWN 脚本,确认能回滚,最后再执行一遍 UP,确保可重复迁移。
这个过程里,Claude Code 也可以帮上忙。我会把测试库执行报错的信息复制给它,让它分析原因并给出修复方案:
执行 ALTER TABLE 时 PostgreSQL 报了如下错误: [贴错误信息] 请分析原因,并给出修复后的完整 ALTER 语句。注意保持和原迁移目标一致。这个反馈循环是我目前用下来最顺手的姿势:让 AI 当执行者和分析者,我来当最终验收者。测试库执行一次通过的迁移脚本,我才有信心提交到代码评审。
4. 迁移脚本的质量把关:这步最不能省
4.1 三个必查点:类型映射、索引选择、约束外键
Claude Code 生成的脚本整体质量不低,但它不会自动理解你项目的历史包袱。我每次审查脚本都会盯着三个固定位置:
第一个是类型映射。不同数据库对类型的处理差异很大,MySQL 的TINYINT(1)和 PostgreSQL 的BOOLEAN语义并不完全对等。如果 Claude Code 生成的是跨数据库方言的脚本,我必须确保类型映射是目标数据库的正确用法。比如 MySQL 8.0 里已经不建议用VARCHAR(255)作为默认字符串长度,如果它生成的是VARCHAR(255),我会主动改成业务实际需要的长度。
第二个是索引选择。AI 看到字段变更后,经常会机械地把索引原样迁移过去,但新字段的索引需求很可能和旧字段不一样。字段拆分后,phone_number做唯一索引合理,但如果业务上还要按country_code + phone_number做联合查询,就应该改成联合唯一索引,这个判断 AI 做不了,得人工决定。
第三个是外键约束。多表结构下,AI 有时会漏掉对某个关联表外键的处理。我要求 Claude Code 在生成脚本时附带"受影响外键清单",然后我用数据库的information_schema查一遍实际外键,两边对照,不一致就让它补。
4.2 让 Claude Code 自己先审一遍 SQL
我有段时间觉得 Claude Code 写完东西不自检,后来发现是我没让它自检。它其实是愿意自查的,只要显式提要求:
请重新审查你生成的 UP 脚本,逐个字段检查: 1. 字段名是否都存在于 users 表中(用 schema.sql 对照) 2. 数据类型是否符合目标数据库方言 3. 索引名是否会与现有索引冲突 4. 外键引用是否完整 5. 是否有遗漏 NOT NULL 或 DEFAULT 约束 发现问题直接告诉我,不需要修改,先列出问题清单。注意这里要求它"先列出问题清单,不要直接改",这个细节很重要。让它直接改的话,它可能修改了 A 处却破坏了 B 处,你很难追踪它改了什么。先列问题,你确认后再让它逐条修复,每一步都是可控、可追溯的。
实测下来,这一步通常能筛掉一大半低级错误。我遇到过的典型问题包括:索引名重复(idx_users_phone已存在)、忘记给新增字段加NOT NULL、拆分字段时漏了CHECK约束等。这些问题如果直接进测试库,错误信息会把排查时间拉长好几倍。
4.3 用 EXPLAIN 和抽样数据验证迁移结果
SQL 脚本语法正确不代表迁移后性能正常。字段拆分、类型变更都会影响执行计划,所以我在测试库验证阶段会跑一轮EXPLAIN,重点看涉及变更字段的查询。
比如订单表状态字段改成枚举类型后,原查询:
SELECT * FROM orders WHERE status = 'pending';我会让 Claude Code 生成验证 SQL 和对应的EXPLAIN:
请生成以下两条 SQL: 1. 查询 orders 表中新增字段 country_code = '+86' 的订单数量,并附 EXPLAIN 结果 2. 查询 users 表 phone_number = '13800138000' 的用户,确认唯一索引是否生效EXPLAIN结果里如果type是ALL(全表扫描),说明索引没建对,需要回炉。这一步能防的性能问题,比后期优化简单得多。
另外我还会做一轮数据抽样验证。迁移完成后,把原字段的数据和新字段的数据抽样对比。拿刚才的 users 表举例,我会运行:
SELECT COUNT(*) AS total, SUM(CASE WHEN '+86' || phone_number = phone THEN 1 ELSE 0 END) AS matched FROM users_backup;这里的思路是:用旧表快照作为基准,检查新表数据是否和旧表逻辑一致。Claude Code 可以帮我写这类校验 SQL,我只需要提供新旧字段的映射规则。
5. 常见问题与排查技巧实录
我把自己用 Claude Code 写迁移脚本过程中遇到的典型问题整理成了一张速查表,每个都是真实踩过的坑,按频率从高到低排:
| 问题现象 | 可能原因 | 解决思路 |
|---|---|---|
| SQL 语法没问题,但执行报字段不存在 | Claude Code 参考了旧 schema,未同步最新结构 | 先把最新 schema.sql 重新读一遍,再让它基于最新结构生成脚本 |
| 索引名重复导致执行中断 | 新生成的索引名与已有索引冲突 | 让它列出全部现有索引名,修改索引命名规则,加上表名/业务前缀 |
| 字符集或排序规则不一致 | 字段类型变更后未指定字符集 | 在提示词里明确CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci这类目标规则 |
| 迁移后数据不一致(数量对不上) | 数据拆分/合并规则描述不清 | 回滚后,重新细化数据映射规则再生成脚本,并在测试库做全量对比 |
| 生成的 UP 和 DOWN 互相不对应 | 回滚规则没描述清楚 | 写清回滚的字段合并/拆分规则,让 DOWN 脚本逆向执行 UP 的每一步 |
| Claude Code 自作聪明改了无关表 | 提示词约束不足 | 加强约束:"只允许操作users表,其他表一律不允许改动" |
| 生成多条 ALTER 语句放在一个事务里导致锁表时间过长 | 未分事务 | 让它按批次拆分事务,每批 5 到 10 条 DDL 提交一次 |
这个表目前是我团队内部讨论迁移脚本时的主要参考,基本能覆盖 Claude Code 写 SQL 脚本的九成翻车现场。
再补充几个自己摸索出来的独门技巧。
一是千万小心 AI 的"幻觉"表名和列名。有一次我让它处理order_items表,它生成的脚本里出现了orders_item这个不存在的表名——不知道是拼写错误还是凭空想象,反正在测试库直接爆表不存在的错误。从那以后,只要是生成的脚本里出现我没在 schema 清单里见过的表名或者字段名,我一律视为幻觉,要求它重新对照information_schema或建表文件核实。
二是在提示词里多用"如果"句式来约束意外情况。比如"如果phone字段存在 NULL 值,请在拆分前用默认值填充",这种条件式描述能让 Claude Code 主动思考边界情况,比我单纯说"处理好 NULL"效果好得多。
三是执行顺序很重要。生成脚本时我明确要求它按依赖顺序排列:先建表、再迁移数据、再删旧字段、最后重建索引和外键。有一次它把删除旧字段放在数据迁移之前,好在测试阶段就发现了,否则数据直接全丢,真不是开玩笑的事。
6. 更进一步:把 Claude Code 迁移脚本嵌入日常流程
6.1 在项目里建立"迁移脚本模板"文件
用的次数多了以后,我发现与其每次重新写提示词,不如在项目里维护一份migration_prompt_template.md模板文件,把常用约束、命名规范、输出格式固化下来。每次需要 Claude Code 写迁移脚本时,直接让它读这个模板,再按模板要求输出。
模板文件内容其实不长,核心就是项目自己的规范,包括:迁移文件命名必须带时间戳前缀、UP 和 DOWN 必须同时存在、禁止使用跨数据库方言、字段变更需要注明变更原因等。这等于是把团队约定变成了 AI 的默认行为,省去了大量重复沟通成本。
6.2 与数据库版本管理工具配合的真实工作流
我目前最顺手的流程是:用 Claude Code 生成初始迁移脚本,人工审核后放进迁移工具(比如 Flyway 或 Liquibase)的目录结构里,然后在测试环境执行,让工具记录校验和。之后任何对迁移脚本的修改,必须重新走一遍生成和审核流程。
这个流程的好处是:AI 负责快速产出,工具负责版本追踪和幂等保障,人工负责最终裁决。三者各司其职,不会出现"AI 生成的脚本直接上线,没人知道是谁写的、怎么变更的"这类失控情况。
6.3 我个人对 AI 写 SQL 的几点体会
用了这段时间,最大的体会不是"AI 写 SQL 有多快",而是"AI 逼我把需求想清楚"。以前我写迁移脚本,很多规则是边写边想的,比如数据拆分遇到 NULL 怎么处理、旧索引到底要不要保留,这些模糊地带最后都靠执行阶段试错来补。但给 Claude Code 写提示词,我必须先把这些问题想明白,因为模糊的提示词会直接变成模糊的脚本。
这个过程虽然前置时间变长了,但整体项目的质量和可预测性反而上来了。我现在写迁移脚本的时间分布大概是:梳理需求占一半,和 AI 交互占三成,人工审阅占两成。以前是上来就写,写一半发现漏了约束,再回头改,实际花的时间比现在多得多。
还有一个小心得:每次 Claude Code 生成的脚本如果发现问题,我会把问题和修复方案写进模板文件的"常见坑点"章节,这个项目越记越多,后续生成的脚本质量就越来越高,已经形成了正向循环。
数据库迁移这件事从最早的手工一条条敲 SQL,到后来用可视化工具对比 Schema,再到现在用 Claude Code 做迁移脚本生成,工具一直在变,但核心逻辑没变:迁移前想清楚,迁移中可回滚,迁移后能验证。Claude Code 帮我省掉了那些重复的、机械的时间,让我能把精力放在真正需要判断力的地方——哪些数据要清洗、哪些索引该保留、哪些外键会影响上下游应用。对一个数据侧开发来说,这才是最值钱的部分。