社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  DATABASE

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

IT牧场 • 5 年前 • 501 次点击  


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

像下面这样:


  • 画图



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



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



  • 生成的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.    com.github.jsqlparser

  2.    jsqlparser

  3.    1.2

写代码:

  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语句。

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


今天看啥 - 高品质阅读平台
本文地址:http://www.jintiankansha.me/t/EYRQrJIO6N
Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/27474
 
501 次点击