首页 > 数据库 >纯分享:将MySql的建表DDL转为PostgreSql的DDL

纯分享:将MySql的建表DDL转为PostgreSql的DDL

时间:2023-09-02 23:11:05浏览次数:85  
标签:COMMENT PostgreSql DEFAULT mysql pg sql MySql NULL DDL

背景

现在信创是搞得如火如荼,在这个浪潮下,数据库也是从之前熟悉的Mysql换到了某国产数据库。

该数据库我倒是想吐槽吐槽,它是基于Postgre 9.x的基础上改的,至于改了啥,我也没去详细了解,当初的数据库POC测试和后续的选型没太参与,但对于我一个开发人员的角度来说,它给我带来的不便主要是客户端GUI工具这块。

我们读写数据库,程序这块还好,CURD代码用到的语法,基本是sql标准兼容的那些,没用多少mysql的特殊语法,所以这块没啥感觉。

客户端GUI这块,pg的客户端软件目前知道几个:

  • navicat,公司没采购正版,用不了,替代软件是开源的dbeaver
  • pgAdmin,pg官方客户端,结果不知道这个国产化过程中改了啥,用pgAdmin连上就各种报错,放弃
  • dbeaver,这个倒是可以用,就是我感觉操作太麻烦了,太繁琐

基于以上原因,一直用dbeaver来着,之前两次把mysql项目的表结构换成pg,一次是写了个乱七八糟的代码来做建表语句转换,一次是用dbeaver建的,太繁琐了。

这次又来了个项目,我就换回了我熟悉的sqlyog(一款mysql客户端),几下就把表建好了(mysql版本),然后写了个工具代码,来把mysql的DDL转换成pg的。

下面简单介绍下这个转换代码。

技术选型

以前写这种代码,都是各种字符串操作(正则、匹配、替换等等),反正代码最终是非常难以维护。这次就先去网上查了下,发现有人有类似需求,还发了文章:https://zhuanlan.zhihu.com/p/314069540

我发现其中利用了一个java库,JSqlParser(https://github.com/JSQLParser/JSqlParser),我在网上也找了下其他的库,java这块没有更好的了,遥遥领先。

其官方说明:

JSqlParser parses an SQL statement and translate it into a hierarchy of Java classes.

它支持解析sql语句这种非结构化文本为结构化数据,比如,针对如下的一个建库sql:

CREATE TABLE `xxl_job_log_report` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `trigger_day` datetime DEFAULT NULL COMMENT '调度-时间',
  `running_count` int(11) NOT NULL DEFAULT '0' COMMENT '运行中-日志数量',
  `suc_count` int(11) NOT NULL DEFAULT '0' COMMENT '执行成功-日志数量',
  `fail_count` int(11) NOT NULL DEFAULT '0' COMMENT '执行失败-日志数量',
  `update_time` datetime DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

可以解析为如下的类及属性:

image-20230902221909233

如上就包含了索引、列定义、建表选项等等。

我们接下来就只需要根据这些字段,获取数据并转换为对应的Postgre的语法即可。

转换效果

源码:https://github.com/cctvckl/convertMysqlDdlToPostgre.git

对于以上的类,给大家看看转换效果:

CREATE TABLE xxl_job_log_report (
    id serial PRIMARY KEY,
    trigger_day timestamp NULL,
    running_count int NOT NULL DEFAULT '0',
    suc_count int NOT NULL DEFAULT '0',
    fail_count int NOT NULL DEFAULT '0',
    update_time timestamp NULL
);
COMMENT ON COLUMN xxl_job_log_report.trigger_day IS '调度-时间';
COMMENT ON COLUMN xxl_job_log_report.running_count IS '运行中-日志数量';
COMMENT ON COLUMN xxl_job_log_report.suc_count IS '执行成功-日志数量';
COMMENT ON COLUMN xxl_job_log_report.fail_count IS '执行失败-日志数量';

这个sql,基本都满足我们的要求了。

当然,我这个工具类,还没特别完善,对于索引这块,只支持了主键索引,其他索引类型,后面空了我补一下。

支持的DDL类型,目前仅限于create table和drop table,目前能满足我个人需求了,反正mysqldump那些导出来的sql结构基本就这样。

暂不支持DML,如insert那些。

代码要点

整体逻辑

Statements statements = CCJSqlParserUtil.parseStatements(sqlContent);
for (Statement statement : statements.getStatements()) {
    if (statement instanceof CreateTable) {
        String sql = ProcessSingleCreateTable.process((CreateTable) statement);
        totalSql.append(sql).append("\n");
    } else if (statement instanceof Drop) {
        String sql = ProcessSingleDropTable.process((Drop) statement);
        totalSql.append(sql).append("\n");
    } else {
        throw new UnsupportedOperationException();
    }
}

如上,CCJSqlParserUtil 是 JSqlParser 的工具类,将我们的sql转换为一个一个的statement(即sql语句),我这边利用instanceof检查属于哪种DDL,再调用对应的代码进行处理,设计模式也懒得弄,if else写起来多快。

数据准备:表注释

List<String> tableOptionsStrings = createTable.getTableOptionsStrings();
String tableCommentSql = null;
int commentIndex = tableOptionsStrings.indexOf("COMMENT");
if (commentIndex != -1) {
    tableCommentSql = String.format("COMMENT ON TABLE %s IS %s;", tableFullyQualifiedName,tableOptionsStrings.get(commentIndex + 2));
}

解析出的表的相关属性,全都被放在一个list中,我们根据COMMENT关键字定位索引,然后找后两个,即是表注释具体值。

数据准备:列注释

由于我是直接在作者基础上改的,https://zhuanlan.zhihu.com/p/314069540,所以也是像他那样,复用了其代码,提取每一列的注释,逻辑也是根据COMMENT关键字找到index,然后index+1就是注释值。

image-20230902224226311

提取出来后,格式化为pg语法:

String.format("COMMENT ON COLUMN %s.%s IS %s;", table, column, commentValue);

数据准备:提取主键

Index primaryKey = createTable.getIndexes().stream()
    .filter((Index index) -> Objects.equals("PRIMARY KEY", index.getType()))
    .findFirst().orElse(null);

组装sql:建表第一行

String createTableFirstLine = String.format("CREATE TABLE %s (", tableFullyQualifiedName);

组装sql:主键列

这里涉及数据类型转换,如mysql中的bigint,在pg中,使用bigserial即可:

String dataType = primaryKeyColumnDefinition.getColDataType().getDataType();
if (Objects.equals("bigint", dataType)) {
    primaryKeyType = "bigserial";
} else if (Objects.equals("int", dataType)) {
    primaryKeyType = "serial";
} else if (Objects.equals("varchar", dataType)){
    primaryKeyType = primaryKeyColumnDefinition.getColDataType().toString();
}

String sql = String.format("%s %s PRIMARY KEY", primaryKeyColumnName, primaryKeyType);

组装sql:其他列

这部分有几块:

  • 类型转换,mysql的类型,转换为pg的,我这边定义了一个map,大致如下:

    image-20230902224901408

​ 以上仅是部分,具体查看代码

  • 默认值处理

    这块也比较麻烦,比如mysql中的函数这种,如CURRENT_TIMESTAMP这种默认值,转换为pg中的对应函数,我大概定义了几个,满足当前需要:

    static {
        MYSQL_DEFAULT_TO_POSTGRE_DEFAULT.put("NULL", "NULL");
        MYSQL_DEFAULT_TO_POSTGRE_DEFAULT.put("CURRENT_TIMESTAMP", "CURRENT_TIMESTAMP");
        MYSQL_DEFAULT_TO_POSTGRE_DEFAULT.put("CURRENT_DATE", "CURRENT_DATE");
        MYSQL_DEFAULT_TO_POSTGRE_DEFAULT.put("CURRENT_TIME", "CURRENT_TIME");
    }
    
  • 删除pg不支持的mysql语法

    // postgre不支持unsigned
    sourceSpec = sourceSpec.replaceAll("unsigned", "");
    // postgre不支持ON UPDATE CURRENT_TIMESTAMP
    sourceSpec = sourceSpec.replaceAll("ON UPDATE CURRENT_TIMESTAMP", "");
    
    

打印完整的pg语法sql

这块就不说了,上面效果展示部分有。

生成出来的sql,会在项目根路径下的target.sql文件中

总结

生成的target.sql文件,在idea中打开,如果有语法错误会飘红,如果大家有java开发能力,直接debug改就行,不行就提issue,我看到了空了就改;

我之前拿着有语法错误的sql就去dbeaver执行了,报错也不详细,看得一脸懵,idea还是厉害。

参考资料

mysql官方的迁移指南,里面包含了pg的各种类型对应到mysql的什么类型
https://dev.mysql.com/doc/workbench/en/wb-migration-database-postgresql-typemapping.html
mysql中的各种类型查阅
https://dev.mysql.com/doc/refman/8.0/en/data-types.html
pg中的各种类型查阅,我看得低版本的,谁让我们的信创数据库是基于pg 9版本的呢
https://www.postgresql.org/docs/11/datatype-numeric.html#DATATYPE-INT

这边直接贴一下吧,方便大家看:

Pg Source Type Taret MySQL Type Comment
INT INT
SMALLINT SMALLINT
BIGINT BIGINT
SERIAL INT Sets AUTO_INCREMENT in its table definition.
SMALLSERIAL SMALLINT Sets AUTO_INCREMENT in its table definition.
BIGSERIAL BIGINT Sets AUTO_INCREMENT in its table definition.
BIT BIT
BOOLEAN TINYINT(1)
REAL FLOAT
DOUBLE PRECISION DOUBLE
NUMERIC DECIMAL
DECIMAL DECIMAL
MONEY DECIMAL(19,2)
CHAR CHAR/LONGTEXT
NATIONAL CHARACTER CHAR/LONGTEXT
VARCHAR VARCHAR/MEDIUMTEXT/LONGTEXT
NATIONAL CHARACTER VARYING VARCHAR/MEDIUMTEXT/LONGTEXT
DATE DATE
TIME TIME
TIMESTAMP DATETIME
INTERVAL TIME
BYTEA LONGBLOB
TEXT LONGTEXT
CIDR VARCHAR(43)
INET VARCHAR(43)
MACADDR VARCHAR(17)
UUID VARCHAR(36)
XML LONGTEXT
JSON LONGTEXT
TSVECTOR LONGTEXT
TSQUERY LONGTEXT
ARRAY LONGTEXT
POINT POINT
LINE LINESTRING
LSEG LINESTRING
BOX POLYGON
PATH LINESTRING
POLYGON POLYGON
CIRCLE POLYGON
TXID_SNAPSHOT VARCHAR

标签:COMMENT,PostgreSql,DEFAULT,mysql,pg,sql,MySql,NULL,DDL
From: https://www.cnblogs.com/grey-wolf/p/17674382.html

相关文章

  • MySQL添加用户并授权
    执行前要确认,授权给这个用户的数据库已经被创建好createdatabase库名charactersetutf8mb4;接下来,可以采用直接授权的方式,能够同时创建用户以及授权grantselect,insert,delete,update,createon库名.*to'用户名'@'%'identifiedby'密码';......
  • 向量搜索技术:基于Elasticsearch/PostgreSQL/Redis扩展的向量搜索数据库或独立向量搜索
    理论基础与研究向量数据库用于非结构化文本、图片、音频、视频搜索、推荐,将他们转换为数字向量表示来进行相似性(ANN)搜索。存储和搜索高维向量是其特征之一,通常采用高级索引技术和算法如HNSW,Annoy,或Faiss来实现。不同于SQL数据库,向量数据库更像nosql,用户接受使用sdk/API......
  • 重复索引检查mysql pt-duplicate-key-checker
    索引对于优化数据库中的查询执行时间至关重要,但是索引数量过多或冗余会对性能产生负面影响。虽然pt-duplicate-key-checker是识别MySQL中重复或冗余索引的首选工具,但它可能无法捕获所有重复。测试环境,mysql5.7.27建表结mysql[localhost:5727]{root}(sbtest)>CREATETABLE`sbt......
  • MYSQL数据库备份还原,并还原到最新状态(mysqldump)
    启用二进制日志文件vim/etc/my.cnf配置文件位置及文件名根据实际情况确定<br>sql_log_bin=on|off:是否记录二进制日志,默认为on在需要的时候设置为off=""<br>log_bin="/PATH/BIN_LOG_FILE:指定二进制日志文件位置;"通常单独存放到与数据库不同的机器中=""<=""p=""></br>......
  • IPV6配置mysql
    一、mysql参数影响mysql配置文件my.cnf可以配置一个参数bind-address该参数默认为*,意思是全网监听(可以是ipv4也可以是ipv6)如果该参数设置成ipv4地址,则mysql的3306端口监听ipv4地址如果该参数设置生ipv6地址,则mysql的3306端口监听ipv6地址二、mysql主从配置CHANGEMASTERTO......
  • MySQL的基本语句
    目录1.0数据库操作2.0数据表操作2.1表的创建2.2表的修改2.2.1表中字段的添加2.2.2表中字段的修改2.2.3表中字段的删除2.3表的查询2.3.1查询数据库中所有的表2.3.2查询表结构2.4表的删除2.5表中数据的操作2.5.1表数据的查询2.5.1.1表中数据的简单查询2.5.1.2表中数据......
  • CentOS6.5安装mysql 远程登录
    第1步、yum安装mysql[[email protected]]#yum-yinstallmysql-server直到出现结果: 第2步、设置开机启动[[email protected]]#chkconfigmysqldon这步没提示第3步、启动mysql服务[[email protected]]#servicemysqldstartshell提示: 第4步、修改r......
  • mysql索引(转)
    转载:https://www.php.cn/faq/493277.html一、数据结构区分1.1.B+tree索引根据存储方式,mysql可以分为B+tree索引和哈希索引B+tree索引可以进行全键值、键值范围和键值前缀查询1.2.哈希索引哈希索引也称为散列索引或 HASH索引。MySQL目前仅有MEMORY存储引擎和HEAP存......
  • mysql decode()
    mysqldecode()    举例:    oracle: select  decode(pay_name, ' aaaa ' , ' bbb ' ,pay_name), sum (comm_order), sum (suc_order), sum (suc_amount)  From   payment.order_tab   group   by  decode(pay_name, ' aaaaa ' , ' bb......
  • 12.mysql数据查询
    下面是一些MySQL数据库中的数据查询操作示例,包括单表查询和多表查询,以及相应的示例数据表。单表查询:假设我们有一个名为products的表,用于存储产品信息:CREATETABLEproducts(product_idINTPRIMARYKEY,product_nameVARCHAR(255),categoryVARCHAR(50),......