CREATE TRIGGER 无法捕获 DDL 变更,因主流数据库触发器仅支持 DML;可行方案是定期快照比对或使用 Flyway/Liquibase 等版本化迁移工具。

为什么不能只靠 CREATE TRIGGER 捕获表结构变更
触发器(TRIGGER)在 MySQL、PostgreSQL 等主流数据库中,只能响应 INSERT、UPDATE、DELETE 这类 DML 操作,**对 ALTER TABLE、DROP COLUMN、ADD INDEX 等 DDL 操作完全无感知**。这意味着你写一个 BEFORE ALTER ON users 触发器——语法直接报错,数据库根本不支持。
所以,想用触发器“自动记录结构变动”,本质上是个伪需求。真实可行的路径只有两条:
- 放弃实时捕获,改用定期快照比对(如每天凌晨跑一次
pg_dump --schema-only或查询information_schema.columns) - 把结构变更纳入人工流程:所有
ALTER必须走 SQL 变更脚本 + 版本库提交,再由部署工具(如 Flyway、Liquibase)执行并记录
用 information_schema 手动抓取当前结构快照
这是最轻量、兼容性最好的方式,适用于 MySQL、PostgreSQL(部分视图名不同)、SQL Server。核心是查系统表,不是监听事件。
例如,导出某张表的字段定义快照(MySQL):
SELECT column_name, data_type, is_nullable, column_default, extra FROM information_schema.columns WHERE table_schema = 'mydb' AND table_name = 'users' ORDER BY ordinal_position;
你可以把这个查询封装成脚本,每次上线前手动运行,保存结果为 users-20240520.json。后续比对就靠文本 diff 工具或简单 Python 脚本。
- 注意
information_schema在 PostgreSQL 中对应pg_attribute+pg_class,字段名和过滤逻辑不同 - MySQL 的
extra字段包含auto_increment,但 PostgreSQL 需查pg_get_serial_sequence() - 索引、约束、外键需额外查
information_schema.key_column_usage等视图,不能只看列
Flyway / Liquibase 是事实标准,别自己造轮子
如果你真需要可追溯、可回滚、带版本号的结构管理,Flyway 和 Liquibase 不是“可选方案”,而是唯一靠谱选择。它们不依赖数据库触发器,而是靠维护一个已执行脚本清单(如 V1__init.sql、V2__add_email_index.sql),每次启动时校验 flyway_schema_history 表。
-
Flyway更轻:只管 SQL 文件顺序执行,适合 DBA 主导的团队 -
Liquibase支持 YAML/JSON 声明式定义,能生成跨数据库兼容的 DDL,适合多环境部署 - 两者都强制要求“每个变更必须有唯一版本号”,天然解决谁改了什么、何时改的问题
- 误删
flyway_schema_history表?后果严重——下次执行会重复跑旧脚本,导致Duplicate column错误
审计 DDL 的唯一可行替代方案:数据库日志 + 解析
极少数场景(如金融合规强审计),确实需要捕获每条 ALTER 语句。这时得绕过触发器,转而依赖数据库自身日志能力:
- PostgreSQL:开启
log_statement = 'ddl',配合pg_log目录轮转 + 日志解析脚本(如用 Python 提取ALTER TABLE.*ADD COLUMN) - MySQL:启用
general_log并设为表存储(log_output = 'TABLE'),查mysql.general_log,但性能开销大,仅限临时审计 - 注意:日志里只有原始 SQL,没有结构前后对比;要还原“改了什么”,仍需搭配快照做 diff
这种方案运维成本高、不可移植、难集成进 CI/CD——除非合规硬性要求,否则优先走 Flyway 脚本化路径。










