ITPub博客

首页 > 数据库 > MySQL > MySQL 迁移 PostgreSQL 时 DDL 需要注意的事项

MySQL 迁移 PostgreSQL 时 DDL 需要注意的事项

原创 MySQL 作者:pingdanorcale 时间:2026-05-24 08:31:16 0 删除 编辑

MySQL 迁移 PostgreSQL 时 DDL 需要注意的事项

MySQL 迁移到 PostgreSQL(PG),最大的问题通常不是数据,而是:

  • DDL兼容性
  • SQL语法差异
  • 数据类型差异
  • 自增机制
  • 索引与约束
  • 函数/过程
  • 大小写规则

很多 MySQL 能执行的 DDL,到了 PostgreSQL 会直接报错。

下面按实际迁移项目经验,整理一个较完整的 DDL 兼容性说明。


一、核心差异总览

项目 MySQL PostgreSQL
大小写 不敏感 敏感
自增 AUTO_INCREMENT SERIAL/IDENTITY
存储引擎 InnoDB/MyISAM 无
字符集 表级/库级 数据库级
ENUM 原生支持 需单独创建TYPE
COMMENT 语法不同 使用 COMMENT ON
UNSIGNED 支持 不支持
ON UPDATE CURRENT_TIMESTAMP 支持 不支持
TINYINT 支持 无
DATETIME 支持 timestamp
双引号 字符串可兼容 双引号表示对象名

二、DDL 迁移最容易出问题的地方


1、自增列处理(最重要)

MySQL


CREATE 
TABLE user_info (

   id BIGINT AUTO_INCREMENT PRIMARY KEY
);

PostgreSQL

PostgreSQL 不支持:

AUTO_INCREMENT

需要改成:

方法1:SERIAL


CREATE 
TABLE user_info (

   id BIGSERIAL PRIMARY KEY
);

方法2:IDENTITY(推荐 PG10+)


CREATE 
TABLE user_info (

   id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY
);

注意事项

很多迁移工具:

  • 只迁数据
  • 不迁 sequence

会导致:

duplicate key value violates unique constraint

因为 sequence 没同步。

迁移后必须:


SELECT setval(

    'user_info_id_seq',
   ( SELECT MAX(id) FROM user_info)
);

2、数据类型差异(高频问题)

(1)TINYINT

MySQL

status TINYINT

PostgreSQL

无 TINYINT。

通常改:

SMALLINT

或者:

BOOLEAN

(2)UNSIGNED

MySQL

id INT UNSIGNED

PostgreSQL

不支持:

UNSIGNED

需要扩大类型:

MySQL PostgreSQL
INT UNSIGNED BIGINT
SMALLINT UNSIGNED INTEGER

(3)DATETIME

MySQL

create_time DATETIME

PostgreSQL

改:

create_time TIMESTAMP

建议:

TIMESTAMP WITHOUT TIME ZONE

或者:

TIMESTAMP WITH TIME ZONE

(4)DOUBLE

建议:

DOUBLE PRECISION

(5)TEXT/BLOB

MySQL

TEXT

LONGTEXT
BLOB

PostgreSQL

通常:

MySQL PostgreSQL
TEXT TEXT
LONGTEXT TEXT
BLOB BYTEA

(6)JSON

MySQL

JSON

PostgreSQL

建议:

JSONB

因为:

  • 支持索引
  • 查询更优

三、字符集与排序规则

MySQL

支持:

CHARSET
=utf8mb4

COLLATE=utf8mb4_general_ci

PostgreSQL

不支持表级:

CHARSET=

不支持:

ENGINE=

这些必须删除。


迁移时常见报错

syntax error at or near "ENGINE"

四、DDL 尾部属性删除

MySQL

ENGINE
=InnoDB

DEFAULT CHARSET =utf8mb4
ROW_FORMAT =DYNAMIC

PG 全部不支持。

必须删掉。


五、关键字冲突(非常常见)

MySQL 很多字段名:


user

order
group
limit
key

在 PG 中是关键字。


MySQL 可执行


CREATE 
TABLE 
order (

   id INT
);

PostgreSQL 报错

需要:


CREATE 
TABLE 
"order" (

   id INT
);

但:

不建议大量双引号。


推荐做法

直接改字段名:

原字段 建议
order order_no
user user_name
group group_name

六、大小写问题(特别重要)

MySQL

默认:

  • 不区分大小写

PostgreSQL

默认:

  • 自动转小写
  • 双引号严格区分大小写

MySQL

CREATE TABLE UserInfo

查询:

select * from USERINFO;

也能成功。


PostgreSQL

会变成:

userinfo

如果:

CREATE TABLE "UserInfo"

以后必须:

SELECT * FROM "UserInfo";

否则报错。


强烈建议

迁移时:

统一转小写。

例如:

user_info

order_detail

七、默认值兼容

MySQL

update_time 
TIMESTAMP 
DEFAULT 
CURRENT_TIMESTAMP

ON UPDATE CURRENT_TIMESTAMP

PostgreSQL

不支持:

ON UPDATE CURRENT_TIMESTAMP

需要:

  • trigger
  • function

实现。


示例


CREATE 
OR REPLACE 
FUNCTION update_modified_column()

RETURNS TRIGGER AS $$
BEGIN
  NEW.update_time = now();
  RETURN NEW;
END;
$$ language 'plpgsql';

八、COMMENT 语法不同

MySQL

name VARCHAR(100) COMMENT '姓名'

PostgreSQL

需要:

COMMENT ON COLUMN user_info.name IS '姓名';

九、索引差异

1、前缀索引

MySQL

CREATE INDEX idx_name ON t(name(20));

PostgreSQL

不支持前缀索引。

需要:

  • 全字段索引
  • expression index

2、全文索引

MySQL

FULLTEXT INDEX

PostgreSQL

使用:

GIN

to_tsvector

实现。


十、外键约束差异

MySQL:

很多系统:

  • 实际未启用外键
  • 或 MyISAM 无外键

PG:

外键更严格。

迁移时:

可能因为脏数据失败。


常见错误

insert or update violates foreign key constraint

建议

迁移前:

先清洗脏数据。


十一、NULL 与空字符串差异

MySQL

有时:

'' == NULL

逻辑混乱。


PostgreSQL

严格区分:


''

NULL

十二、函数兼容问题

MySQL 函数

MySQL PostgreSQL
IFNULL COALESCE
NOW() NOW()
SYSDATE() CURRENT_TIMESTAMP
CONCAT CONCAT
GROUP_CONCAT STRING_AGG
DATE_FORMAT TO_CHAR

十三、存储过程与函数

这是迁移难点之一。


MySQL

DELIMITER 
//

CREATE PROCEDURE

PostgreSQL

使用:

LANGUAGE plpgsql

语法差异很大。

通常:

  • 无法自动转换
  • 需要人工改写

十四、触发器差异

MySQL:

BEFORE INSERT

语法较简单。

PG:

需要:

  • function
  • trigger

分开创建。


十五、分区表差异

MySQL:

PARTITION BY HASH

PG:

支持:

PARTITION BY RANGE/LIST/HASH

但:

实现机制不同。

很多不能直接迁移。


十六、迁移工具常见问题

工具 问题
pgloader DDL转换不完整
ora2pg 更偏Oracle
DataX 不处理DDL
DTS 只同步数据
自研脚本 关键字遗漏

十七、推荐迁移流程

导出 MySQL DDL

       ↓
DDL 自动转换
       ↓
人工修正
       ↓
PG 测试执行
       ↓
迁移数据
       ↓
修正 sequence
       ↓
验证索引约束
       ↓
业务联调

十八、生产迁移重点检查项

必查

□ AUTO_INCREMENT

□ ENGINE=
□ CHARSET=
□ COLLATE=
□ ON UPDATE CURRENT_TIMESTAMP
□ UNSIGNED
□ TINYINT
□ ENUM
□ COMMENT
□ 关键字冲突
□ 大小写问题
□ sequence同步
□ 外键脏数据
□ 存储过程
□ trigger
□ function

十九、推荐规范(非常重要)

推荐 PostgreSQL 风格

表名

user_info

order_detail

不要:

UserInfo

字段名

统一:

  • 小写
  • 下划线

避免双引号

不要:

"UserInfo"

否则后续开发很痛苦。


二十、最终建议

如果系统复杂:

建议:

  • 先迁DDL
  • 再迁数据
  • 最后改业务SQL

最难迁移部分

通常是:

难度 内容
高 存储过程
高 trigger
高 function
中 动态SQL
中 JSON SQL
中 分区
低 普通表

二十一、推荐工具

推荐组合

场景 工具
DDL转换 pgloader
数据迁移 DataX
实时同步 Canal + Kafka + PG
全量校验 checksum
SQL兼容检查 自研脚本

二十二、典型 DDL 转换示例

MySQL


CREATE 
TABLE user_info (

   id BIGINT AUTO_INCREMENT PRIMARY KEY,
   name VARCHAR( 100) NOT NULL,
   status TINYINT DEFAULT 0,
   create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
   update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
        ON UPDATE CURRENT_TIMESTAMP
) ENGINE =InnoDB DEFAULT CHARSET =utf8mb4;

PostgreSQL


CREATE 
TABLE user_info (

   id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
   name VARCHAR( 100) NOT NULL,
   status SMALLINT DEFAULT 0,
   create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
   update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

再增加 trigger 自动更新时间。


二十三、经验总结

真正的难点不是:

  • 数据迁移

而是:

  • SQL兼容
  • DDL兼容
  • 应用兼容

尤其:

  • Java ORM
  • MyBatis动态SQL
  • 存储过程
  • 分页SQL

这些往往问题最多。


来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/10201716/viewspace-3171995/ ,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论

注册时间:2009-01-02

  • 博文量
    158
  • 访问量
    221183