MySQL建表语句转PostgreSQL建表语句全纪录

Posted IT牧场

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MySQL建表语句转PostgreSQL建表语句全纪录相关的知识,希望对你有一定的参考价值。


个人习惯用mysql workbench EER数据建模,然后生成SQL语句到数据库中执行,这样表之间的关系比较直观。

像下面这样:


  • 画图

    MySQL建表语句转PostgreSQL建表语句全纪录



  • 正向工程,生成DDL语句:

    MySQL建表语句转PostgreSQL建表语句全纪录



  • 忽略生成外键,以及外键索引啥的:



  • 生成的DDL语句:



  • 到数据库执行。


踩坑了

最近团队微调,我被调整到另一个小团队。前两天接了个新需求,于是我依然使用MySQL workbench EER建模,结果好不容易建模完成了,却被告知这个项目用的数据库是PostgreSQL!

于是就面临如下几种选择:

  • 重新找个支持导出PostgreSQL DDL语句的建模软件,再弄一遍。据我所知,macOS平台里没啥好的数据建模软件…

  • PowerDesigner用不了(除非装虚拟机,或者Wine);

  • Navicat太难用了(居然有人说Navicat是最好的数据库客户端,我只能给一个大写的服,在我看来,这货连IDEA自带数据库管理都比不上……这观点可能有点偏激,但现状是我做个查询,Navicat把查询按钮藏得很深);

  • IDEA宣布会开发类似功能,但一直没有动静;

  • 开源的PDMan,体验挺不错,但也得连个数据库控制版本。

  • 依然用MySQL workbench导出DDL,然后自己将MySQL DDL转换成PostgreSQL DDL。

我选择了自己转换SQL语句。

开源的DDL转换工具

既然要转换SQL语句,我心想,业界肯定有相关的工具啊。于是上万能的GayHub搜了下,还真有,列出来:

  • mysql-to-postgres:https://github.com/maxlapshin/mysql2postgres

  • mysql-postgresql-converter:https://github.com/lanyrd/mysql-postgresql-converter

  • 多款工具配合使用:https://yq.aliyun.com/articles/241 (不得不佩服这兄弟真有耐心啊!)

然而试用后,内心是崩溃的……生成出来的DDL要么有误,要么没有注释。

自己开发工具

考虑到我的诉求其实非常简单,只是个DDL语句转换而已,自己开发一个也不难。而且之前研读Mybatis通用Mapper源码时,知道Java世界里有个 jsqlparser 的工具。

花了10分钟简单了解了下 jsqlparser 后,就开撸开发工具了……花了20分钟,初版写完了,然后和该项目的同事又花了20分钟验证了下,最终确定了如下的版本。代码贴出来:

加依赖:

 
   
   
 
  1. <dependency>

  2.    <groupId>com.github.jsqlparser</groupId>

  3.    <artifactId>jsqlparser</artifactId>

  4.    <version>1.2</version>

  5. </dependency>

写代码:

 
   
   
 
  1. public class MysqlDdl2PgDdlUtil {

  2.    public static void main(String[] args) throws IOException, JSQLParserException {

  3.        // 你的MySQL DDL路径

  4.        String mysqlDDLPath = "/Users/reno/Downloads/mysql.sql";

  5.        String dDLs = FileUtils.readFileToString(new File(mysqlDDLPath));


  6.        System.out.println(dDLs);

  7.        System.out.println("++++++++++开始转换SQL语句+++++++++++++");


  8.        Statements statements = CCJSqlParserUtil.parseStatements(dDLs);


  9.        statements.getStatements()

  10.                .stream()

  11.                .map(statement -> (CreateTable) statement).forEach(ct -> {

  12.            Table table = ct.getTable();

  13.            List<ColumnDefinition> columnDefinitions = ct.getColumnDefinitions();

  14.            List<String> comments = new ArrayList<>();

  15.            List<ColumnDefinition> collect = columnDefinitions.stream()

  16.                    .peek(columnDefinition -> {

  17.                        List<String> columnSpecStrings = columnDefinition.getColumnSpecStrings();


  18.                        int commentIndex = getCommentIndex(columnSpecStrings);


  19.                        if (commentIndex != -1) {

  20.                            int commentStringIndex = commentIndex + 1;

  21.                            String commentString = columnSpecStrings.get(commentStringIndex);


  22.                            String commentSql = genCommentSql(table.toString(), columnDefinition.getColumnName(), commentString);

  23.                            comments.add(commentSql);

  24.                            columnSpecStrings.remove(commentStringIndex);

  25.                            columnSpecStrings.remove(commentIndex);

  26.                        }

  27.                        columnDefinition.setColumnSpecStrings(columnSpecStrings);

  28.                    }).collect(Collectors.toList());

  29.            ct.setColumnDefinitions(collect);

  30.            String createSQL = ct.toString()

  31.                    .replaceAll("`", "\"")

  32.                    .replaceAll("BIGINT UNIQUE NOT NULL AUTO_INCREMENT", "BIGSERIAL PRIMARY KEY")

  33.                    .replaceAll("BIGINT NULL AUTO_INCREMENT", "BIGSERIAL PRIMARY KEY")

  34.                    .replaceAll("BIGINT NOT NULL AUTO_INCREMENT", "BIGSERIAL PRIMARY KEY")

  35.                    .replaceAll("INT NOT NULL AUTO_INCREMENT", "BIGSERIAL PRIMARY KEY")

  36.                    .replaceAll("INT NULL AUTO_INCREMENT", "BIGSERIAL PRIMARY KEY")

  37.                    .replaceAll("IF NOT EXISTS", "")

  38.                    .replaceAll("TINYINT", "SMALLINT")

  39.                    .replaceAll("DATETIME", "TIMESTAMP")

  40.                    .replaceAll(", PRIMARY KEY \\(\"id\"\\)", "");


  41.            // 如果存在表注释

  42.            if (createSQL.contains("COMMENT")) {

  43.                createSQL = createSQL.substring(0, createSQL.indexOf("COMMENT"));

  44.            }

  45.            System.out.println(createSQL + ";");


  46.            comments.forEach(t -> System.out.println(t.replaceAll("`", "\"") + ";"));

  47.        });

  48.    }


  49.    /**

  50.     * 获得注释的下标

  51.     *

  52.     * @param columnSpecStrings columnSpecStrings

  53.     * @return 下标

  54.     */

  55.    private static int getCommentIndex(List<String> columnSpecStrings) {

  56.        for (int i = 0; i < columnSpecStrings.size(); i++) {

  57.            if ("COMMENT".equalsIgnoreCase(columnSpecStrings.get(i))) {

  58.                return i;

  59.            }

  60.        }

  61.        return -1;

  62.    }


  63.    /**

  64.     * 生成COMMENT语句

  65.     *

  66.     * @param table        表名

  67.     * @param column       字段名

  68.     * @param commentValue 描述文字

  69.     * @return COMMENT语句

  70.     */

  71.    private static String genCommentSql(String table, String column, String commentValue) {

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

  73.    }

  74. }

如代码所示,目前是借助 jsqlparser 的SQL解析能力配合字符串替换的方式生成PostgreSQL的。

效果演示

转换前的DDL:

 
   
   
 
  1. -- -----------------------------------------------------

  2. -- Table `user`

  3. -- -----------------------------------------------------

  4. CREATE TABLE IF NOT EXISTS `user` (

  5.  `id` INT NOT NULL AUTO_INCREMENT COMMENT 'id',

  6.  `username` VARCHAR(16) NOT NULL COMMENT '用户名',

  7.  `email` VARCHAR(255) NULL COMMENT '邮件',

  8.  `password` VARCHAR(32) NOT NULL COMMENT '密码',

  9.  `create_time` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',

  10.  PRIMARY KEY (`id`));



  11. -- -----------------------------------------------------

  12. -- Table `movie`

  13. -- -----------------------------------------------------

  14. CREATE TABLE IF NOT EXISTS `movie` (

  15.  `id` INT NOT NULL AUTO_INCREMENT COMMENT 'Id',

  16.  `name` VARCHAR(255) NOT NULL COMMENT '名称',

  17.  `user_id` INT NOT NULL COMMENT 'user.id',

  18.  PRIMARY KEY (`id`))

  19. COMMENT = '电影表';

转换后的DDL:

 
   
   
 
  1. CREATE TABLE "user"

  2. (

  3.  "id"          BIGSERIAL PRIMARY KEY,

  4.  "username"    VARCHAR(16)  NOT NULL,

  5.  "email"       VARCHAR(255) NULL,

  6.  "password"    VARCHAR(32)  NOT NULL,

  7.  "create_time" TIMESTAMP    NULL DEFAULT CURRENT_TIMESTAMP

  8. );

  9. COMMENT ON COLUMN "user"."id" IS 'id';

  10. COMMENT ON COLUMN "user"."username" IS '用户名';

  11. COMMENT ON COLUMN "user"."email" IS '邮件';

  12. COMMENT ON COLUMN "user"."password" IS '密码';

  13. COMMENT ON COLUMN "user"."create_time" IS '创建时间';

  14. CREATE TABLE "movie"

  15. (

  16.  "id"      BIGSERIAL PRIMARY KEY,

  17.  "name"    VARCHAR(255) NOT NULL,

  18.  "user_id" INT          NOT NULL

  19. );

  20. COMMENT ON COLUMN "movie"."id" IS 'Id';

  21. COMMENT ON COLUMN "movie"."name" IS '名称';

  22. COMMENT ON COLUMN "movie"."user_id" IS 'user.id';

效果还是不错的,基本达到了我的要求。

不足

目前工具代码比较屎,如果想要改进,应该是要让工具理解MySQL DDL的词法,然后构建成例如Table、Column、Comment、Constraint、Index等对象例如:

 
   
   
 
  1. class Table {

  2.    private String name;

  3.    private Column column;

  4. }

  5. class Column {

  6.    private String name;

  7.    private String type;

  8.    // 约束,例如非空等

  9.    private Set<Constraint> constraints;

  10.    // 索引

  11.    private Index index;

  12. }

  13. class Index {

  14.    private String name;

  15.    private String type;

  16. }

  17. enum Constraint {

  18.    NOT_NULL,...;

  19. }

然后抽象一个方言枚举,并为不同的方言制作一个DDL Generator Handler,然后根据不同的方言生成不同数据库平台的DDL语句。

为什么不改进?因为没有时间,工具是为工作服务的,目前能达到我的目的,就没动力修改了,未来有需求再改进吧。

以上是关于MySQL建表语句转PostgreSQL建表语句全纪录的主要内容,如果未能解决你的问题,请参考以下文章

MySQL数据同步到HIVE,自动生成MySQL表对应HIVE的建表语句

oracle建表语句

PostgreSQL获取建表语句存储过程

用SQL语句获得PostgreSQL表的主键

oracle建表语句

怎样用oracle查建表语句