写 SQL 迁移脚本,还不如直接像 Git 一样 Diff?
如果你写过后端,大概率经历过这种痛苦:为了给数据库加个字段,得手写一段 ALTER TABLE,还得小心翼翼地保证在「测试环境」和「生产环境」都能顺利跑通。如果哪个环境落后了几个版本,或者数据库表结构被谁偷偷改过,那就更是灾难现场。
最近,一个叫 sqldef 的工具在开发者圈子里火了一把。它的宣传语简单粗暴:「别写迁移脚本了,像代码比对一样比对数据库吧。」这听起来很美,但当你真的把它扔进生产环境,真的能像 git diff 那样优雅吗?不少尝试过的大佬们在评论区里抖出了一地鸡毛。
什么是「声明式」的数据库管理?
sqldef 的核心玩法非常极客:它是一个命令行工具(CLI),你不需要告诉它「第一步做什么,第二步做什么」。你只需要给出两张蓝图——一张是数据库现在的样子,一张是你期望的样子——它就会自动计算出中间的差异,生成标准的 SQL DDL 语句来填补这个鸿沟。
这种模式在 HN 的讨论里被称为 「声明式 Schema 管理」。一位名为 evanelias 的网友打了个很形象的比方:这就像 React 和 jQuery 的区别。jQuery(命令式)需要你一步步操作 DOM,而 React(声明式)只需要你告诉它 UI 最后长什么样,框架自己会去处理中间的脏活累活。
对于开发者来说,诱惑是巨大的。你把想要的表结构定义扔进 Git 仓库,剩下的事儿交给工具搞定。不用维护成百上千个按时间顺序排列的迁移文件,也不用担心某次部署失败后,回滚脚本写得对不对。
当工具遇到「改名」难题:是改名还是新建?
听起来很完美?别急,现实很快就打了脸。
如果你用 sqldef 修改一个列名,工具的第一反应通常是:「哦,原来这里少了个旧列,多了个新列。」 于是它生成的脚本往往是先 DROP COLUMN 删掉旧列,然后 ADD COLUMN 加个新列。
这对数据库来说逻辑没毛病,但对于数据来说就是灭顶之灾——旧列里的数据全没了。
这就是声明式工具最大的阿喀琉斯之踵:它不知道你的意图,只能机械地比对结构。虽然在 sqldef 的文档里可以通过加特殊注释来规避这个问题,但在实际操作中,这很容易被遗忘。
有经验的开发者指出,在生产环境中,重命名列或表本身就是个高危操作。因为你很难在同一瞬间让所有应用服务器停止旧代码并上线新代码,中间的时间差会导致读取错误。所以很多大厂干脆禁止直接重命名,而是走一套繁琐的「脱机」流程,或者干脆建新列,分两步迁数据。
它不管你的数据,只管你的框架
除了重命名,另一个让人尴尬的场景是 数据迁移。
比如,你把一个 FullName 字段拆成了 FirstName 和 LastName。sqldef 能敏锐地发现结构变化,生成删除旧列、添加新列的 SQL。但是,原来那一百万行的旧数据怎么办?怎么把它们拆开填进去?
工具管不了这个,因为它不懂你的业务逻辑。Hacker News 上的一位网友吐槽说,这种情况下通常还是得写一个一次性的脚本,或者在应用代码里写逻辑来修补空缺。这就像装修队只负责拆墙和砌墙,至于家里的家具怎么搬进来,你得自己想办法。
SQLite 和 PostgreSQL 的「水土不服」
还有用户发现,虽然 sqldef 宣称支持多种数据库,但对某些特殊情况的处理还是显得有点稚嫩。
有位网友拿 SQLite 做实验,尝试给已有表添加外键约束。工具很自信地甩出了一句: ALTER TABLE books ADD CONSTRAINT …
熟悉 SQLite 的朋友看到这里估计要笑了:SQLite 本身就不支持直接给现有表加外键(直到最近某些版本才勉强支持,或者需要重建表)。工具虽然语法没错,但在数据库层面根本跑不通,属于「无效医疗」。
还有人吐槽,对于 SQLite 这种对 DROP COLUMN 支持有限的数据库,工具有时会直接跳过操作(– Skipped),或者生成的脚本在老旧设备上行不通。毕竟 SQLite 经常被嵌入到各种客户端设备里,升级个数据库引擎版本可不是想升就升的。
既然不完美,为什么大家还要用?
尽管槽点不少,但像 sqldef、Skeema(专门针对 MySQL)、Atlas 这类工具依然被大量使用。甚至还有像 pg_roll 这样更高级的工具,试图通过创建视图来实现零停机迁移。
原因很简单:对于 90% 的常规增删改查,它太好用了。
它能极好地解决「Schema Drift」(模式漂移)问题——比如你本地开发环境加了索引,结果上线前忘同步了,这种工具一比对就能揪出差异。对于拥有成千上万数据库分片的公司,靠人肉去核对结构是不可能的,必须依赖自动化工具。
更有意思的是,很多团队采取了「混合双打」的策略:在本地开发环境使用声明式工具,想怎么改就怎么改,追求效率;到了要上生产环境时,让工具生成标准的 SQL 迁移文件,人工审核一遍(确保数据迁移逻辑都在)再入库。这也是像 Prisma 和 Drizzle 等 ORM 现在流行的做法。
总结
sqldef 不是银弹,它更像一把极其锋利的手术刀。如果你只做结构变更,它能帮你省下大量写 SQL 的时间;但一旦涉及数据搬家、重命名这类需要上下文理解的精细操作,你还是得握紧手术刀,亲自上手。
至于有人问:既然这些工具这么麻烦,是不是还不如直接把 Schema 喂给 ChatGPT 生成迁移脚本?还真别说,评论区里有位老兄表示他在用 GPT,而且「效果惊人地好」。看来,在 2026 年,怎么管理数据库,依然是一门融合了工具与玄学的艺术。



