V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
爱意满满的作品展示区。
KURANADO
V2EX  ›  分享创造

写了个命令版的表结构差异对比工具(仅限于 MariaDB)

  •  
  •   KURANADO · 3 天前 · 736 次点击

    项目上不同环境发版需要对比表结构差异,为此经常需要手动点击 Navicat 的表结构同步功能进行数据库比对,生成 create table 、drop table 、alter table 等语句

    为了自动化比对,封装了一个命令行版的对比工具,在 shell 脚本中调用,一键输出同步语句。

    例如初始情况下 a_chemab_schema 均有如下 ddl:

    create table user (
                          id bigint comment '主键',
                          name varchar(30) null comment '姓名',
                          address varchar(50) null comment '地址',
                          number varchar(20) null comment '编号',
                          height float null comment '身高'
    );
    
    create table teacher (
                             name varchar(30) null comment '姓名'
    );
    
    create index idx_name on user(name);
    
    create index idx_multiple_field on user(name, address);
    
    create table course (
                            id      bigint primary key auto_increment comment '主键',
                            name    varchar(30) null comment '课程名称',
                            teacher varchar(30) null comment '教师',
                            credit  float       null comment '学分'
    );
    
    create table pig (
                         id bigint not null comment '名称',
                         weight bigint not null comment '重量'
    );
    

    此时对 a_schema 做如下修改:

    use a_schema;
    alter table user modify column id bigint primary key auto_increment not null comment '主键';
    alter table user add column age int default 18 null comment '年龄';
    alter table user add column create_time datetime not null default current_timestamp comment '创建时间';
    alter table user modify column address varchar(100) not null default 'Shanghai' comment '地址';
    alter table user change column number phone varchar(20) null comment '电话号码';
    alter table user drop column height;
    alter table user add unique index uk_phone(phone);
    alter table user drop index idx_name;
    create table student(
                            id bigint primary key auto_increment comment '主键',
                            no varchar(30) null comment '学号',
                            name varchar(30) null comment '姓名'
    );
    create unique index uk_no on student(no);
    drop index idx_multiple_field on user;
    create index idx_multiple_field on user(name, phone);
    drop table course;
    alter table user add unique index uk_test(age, create_time);
    alter table student add index idx_name(name);
    alter table teacher add COLUMN `id` bigint NOT NULL primary key AUTO_INCREMENT COMMENT '主键' FIRST;
    alter table pig add unique index uk_id_weight(id, weight);
    alter table pig modify column id bigint not null primary key auto_increment comment '名称';
    

    执行命令将会对比表结构差异后输出同步语句:

    $ ddl-diff \
    --original-user root \
    --original-password 123456 \
    --original-host 127.0.0.1 \
    --original-schema a_schema \
    --target-user root \
    --target-password 123456 \
    --target-host 127.0.0.1 \
    --target-schema b_schema
    
    use b_schema;
    CREATE TABLE `student`(
                              `id` bigint(20) NOT NULL  PRIMARY KEY AUTO_INCREMENT COMMENT '主键',
                              `no` varchar(30) NULL DEFAULT NULL  COMMENT '学号',
                              `name` varchar(30) NULL DEFAULT NULL  COMMENT '姓名',
                              INDEX `idx_name` (`name`) USING BTREE,
                              UNIQUE INDEX `uk_no` (`no`) USING BTREE);
    ALTER TABLE `user` DROP INDEX `idx_name`;
    ALTER TABLE `user` DROP INDEX `idx_multiple_field`;
    ALTER TABLE `user` ADD COLUMN `phone` varchar(20) NULL DEFAULT NULL  COMMENT '电话号码' AFTER `address`;
    ALTER TABLE `user` ADD COLUMN `age` int(11) NULL DEFAULT 18  COMMENT '年龄' AFTER `phone`;
    ALTER TABLE `user` ADD COLUMN `create_time` datetime NOT NULL DEFAULT current_timestamp()  COMMENT '创建时间' AFTER `age`;
    ALTER TABLE `user` MODIFY COLUMN `id` bigint(20) NOT NULL  PRIMARY KEY AUTO_INCREMENT COMMENT '主键' FIRST;
    ALTER TABLE `user` MODIFY COLUMN `address` varchar(100) NOT NULL DEFAULT 'Shanghai'  COMMENT '地址' AFTER `name`;
    ALTER TABLE `user` DROP COLUMN `height`;
    ALTER TABLE `user` DROP COLUMN `number`;
    ALTER TABLE `user` ADD INDEX `idx_multiple_field` (`name`,`phone`) USING BTREE;
    ALTER TABLE `user` ADD UNIQUE INDEX `uk_test` (`age`,`create_time`) USING BTREE;
    
    ALTER TABLE `user` ADD UNIQUE INDEX `uk_phone` (`phone`) USING BTREE;
    ALTER TABLE `pig` MODIFY COLUMN `id` bigint(20) NOT NULL  PRIMARY KEY AUTO_INCREMENT COMMENT '名称' FIRST;
    
    ALTER TABLE `pig` ADD UNIQUE INDEX `uk_id_weight` (`id`,`weight`) USING BTREE;
    ALTER TABLE `teacher` ADD COLUMN `id` bigint(20) NOT NULL  PRIMARY KEY AUTO_INCREMENT COMMENT '主键' FIRST;
    
    DROP TABLE IF EXISTS `course`;
    

    目前存在的缺点有:

    • 如果使用的是 MySQL 数据库,则无法正确区分 default 表达式中的普通字符串和 MySQL 内置函数(问题原因是 MySQL INFORMATION_SCHEMA.COLUMNS 中并未对字符串和函数做区分。一种解决方法是通过将 SHOW CREATE TABLE xxx 的返回结果解析成 AST ,sqlparser crate 就具有这种解析能力); MariaDB 无此问题。
    • 无法区分字段重命名的情况,只能输出先删除字段、后新增字段的 alter table 语句。这是因为 INFORMATION_SCHEMA.COLUMNS 没有存储字段 id 。Navicat 、DataGrip 等工具的表结构差异对比都有此问题,所以在执行返回的语句之前,请务必仔细确认之后再执行。如果有小伙伴知道如何解决该问题,欢迎跟帖讨论。

    源码地址:https://github.com/KURANADO2/ddl-diff

    2 条回复    2025-03-07 18:40:03 +08:00
    jybox
        1
    jybox  
       3 天前
    之前用过这个工具 https://www.skeema.io
    很适合在几个环境之间同步表结构
    KURANADO
        2
    KURANADO  
    OP
       3 天前
    @jybox 好东西
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3330 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 22ms · UTC 11:47 · PVG 19:47 · LAX 04:47 · JFK 07:47
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.