nodejs实现比对两个数据库之间的差异

nodejs实现比对两个数据库之间的差异

月光魔力鸭

2023-03-02 16:15 阅读 309 喜欢 0

产品版本更新的时候经常会有一些数据库的差异,如果版本管理好的话,一步一步升级即可.. 但是如果好久没更新的话,还是有很多不确定的,只能挨着比对表和字段。比对了一次就烦了,写了这么一个工具,查询差异表和字段并给出sql语句。

mysql数据库比对工具

nodejs实现

依赖: thinksql

直接放代码

/**
 * 比对两个数据库之间的表结构等差异化,并给出对应的sql 变更
 * 用于版本升级数据库变更
 */
const thinksql = require('thinksql');
const fs = require('fs');
let sqlFile = './diff.sql';//存储差异sql文件
/**
 * 主数据库(开发最全的或者当前版本的)
 */
var masterDB = {
  database: '数据库名称',
  host: 'localhost',
  port: '3306',
  user: 'root',
  password: 'root',
  modelName: 'mysql',
  debounce:false,//防止同语句返回同结果
  // logSql:true
};

/**
 * 需要更新的目标数据库
 */
var updateDB = {
  database: '另一个数据库名称',
  host: 'localhost',
  port: '3306',
  user: 'root',
  password: 'root',
  modelName: 'mysql2',
  debounce:false,
  // logSql:true
};


/**
 * 比对目标
 * 1.表差异
 * 2.字段差异
 * 3.字段类型及注释差异
 */

class DBDiff{

  constructor(master,update) {
    this.master = this.getModel(master);
    this.update = this.getModel(update);

    return this;
  }
  //获取model
  getModel(dbConfig) {
    return thinksql(dbConfig);
  }
  //获取表名
  async getTables(model) {
    let rst = await model.model('').query('show tables');
    return rst.map(t => {let tn = '';for (let k in t) {tn = t[k]}return tn;});
  }
  //获取表结构
  async getColumn(model,tableName) {
    let rst = await model.model('').query(`select * from information_schema.columns where table_schema='${model.config.database}' and table_name='${tableName}'`);
    return rst;
  }
  //根据字段编写建表语句
  getCreateTableSql(tableName, columnList) {
    //获取主键
    columnList.sort((a, b) => {
      return a.ORDINAL_POSITION - b.ORDINAL_POSITION;
    });
    let primaryKey = '';
    let keySql = '';
    columnList.forEach(t => {
      if (t.COLUMN_KEY == 'PRI') {
        primaryKey = t.COLUMN_NAME;
      }
      keySql += `\`${t.COLUMN_NAME}\` ${t.COLUMN_TYPE} ${t.COLLATION_NAME ? 'CHARACTER SET '+t.CHARACTER_SET_NAME+' COLLATE '+t.COLLATION_NAME+'' : ''} ${t.IS_NULLABLE == 'NO' ? ' NOT NULL ' : ' NULL '} ${t.COLUMN_KEY == 'PRI'||t.IS_NULLABLE=='NO' ? '' : 'DEFAULT '+(t.COLUMN_DEFAULT ? t.COLUMN_DEFAULT : 'NULL')}  COMMENT '${t.COLUMN_COMMENT}', \r\n`
    });

    keySql += `PRIMARY KEY (\`${primaryKey}\`) USING BTREE`;
    

    let sql = `
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

DROP TABLE IF EXISTS \`${tableName}\`;
CREATE TABLE \`${tableName}\` (
${keySql}
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_bin ROW_FORMAT = Dynamic;
    `;
    return sql;
  }
  //表字段差异修改
  getColumnSql(tableName,masterList,updateList) {
    //1.有没有,类型是否一致
    var map = {};
    let sql = '';
    updateList.forEach(t => {
      map[t.COLUMN_NAME] = t;
    })
    for (let t of masterList){
      if(!map[t.COLUMN_NAME]){
        //不存在该字段,增加字段数据
        sql += `alter table ${tableName} add column ${t.COLUMN_NAME} ${t.COLUMN_TYPE} default ${t.COLUMN_DEFAULT} comment '${t.COLUMN_COMMENT}';\r\n`;
      } else {
        //判断是否一致
        var tt = map[t.COLUMN_NAME];
        if (t.COLUMN_TYPE != tt.COLUMN_TYPE || t.COLUMN_COMMENT != tt.COLUMN_COMMENT) {
          sql += `alter table ${tableName} modify column ${t.COLUMN_NAME} ${t.COLUMN_TYPE} default ${t.COLUMN_DEFAULT} comment '${t.COLUMN_COMMENT}';\r\n`
        }
      }
    }
    return sql;
  }
  async output(sql,comment) {
    fs.appendFileSync(sqlFile, '-- ' + comment + '\r\n' + sql + '\r\n');
  }

  async start() {

    let masterTables = await this.getTables(this.master);
    let updateTables = await this.getTables(this.update);
    let updateMap = {};
    for (let tableName of updateTables){
      let columnList = await this.getColumn(this.update, tableName);
      updateMap[tableName] = columnList;
    }

    for (let tableName of masterTables){
      console.log(`比对: ${tableName}`)
      let masterColumnList = await this.getColumn(this.master, tableName);
      
      if (!updateMap[tableName]) {
        //表不存在,增加建表语句
        let sql = this.getCreateTableSql(tableName, masterColumnList);
        this.output(sql,'表不存在:'+tableName)
      } else {
        //如果存在则对比字段差异
        let sql = this.getColumnSql(tableName,masterColumnList, updateMap[tableName]);
        this.output(sql, '表字段差异:' + tableName);
      }
    }
  }
}

async function start() {
  let dif = new DBDiff(masterDB, updateDB);
  await dif.start();
  process.exit(0);
}
start().catch(console.error);

最后生成的内容如下:

差异sql


最近写的属实少了点,尽量多写点,哪怕水呢,先上量,在上质。

转载请注明出处: https://chrunlee.cn/article/diff-database-column.html


感谢支持!

赞赏支持
提交评论
评论信息 (请文明评论)
暂无评论,快来快来写想法...
推荐
在我们做运维或者小工具的时候,总会有些需要提醒的事情,比如服务器宕机或者天气提醒,但是发email又会不够及时或者可能会忽略,那么短信就是一个不错的选择了
最近看到知乎上一话题:微信公众号文章里的视频怎么下载?。看还是有很多人推荐啥工具啊,很是捉急,当然本次的主题也是通过程序来获取内容,但是目前来说仅仅是娱乐吧。
互联网应用经常需要存储用户上传的图片,比如facebook相册。 facebook目前存储了2600亿张照片,总大小为20PB,每张照片约为80KB。用户每周新增照片数量为10亿。(总大小60TB),平均每秒新增3500张照片(3500次写请求),读操作峰值可以达到每秒百万次
今天写文章,突然发现自己常用的素材站换成了webp格式的图片.. 可惜本站还没准备加这个支持,所以准备加个webp转jpg的小功能,继续使用啦。
在使用puppeteer 跳转窗口的时候,发现waitForNavigator 并不起作用,最后找到通过browser 获得page 并继续操作。
使用nodejs 连接mysql数据库还是很简单的,有现成的模块可以直接调用。下面介绍下 mysql 的调用
因为自己的记录笔记的应用是有道云,又想着把有道云跟自己的小网站联通起来,所以查找了有道云的,然后实现了nodejs版本的sdk.
对于开发来说,看到别人家的小程序都这么靓,这么顺畅,这么好用,用户又多... 自然是眼馋的..用户馋不来,可以先馋他的身子..啊不,代码啊。