# mysql2pg **Repository Path**: hslei/mysql2pg ## Basic Information - **Project Name**: mysql2pg - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-08-04 - **Last Updated**: 2026-08-04 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # mysql2pg — MySQL → PostgreSQL 单表数据迁移工具 把 MySQL 的表数据迁移到 PostgreSQL:**两个命令**(extract → generate)生成 PG 语法的 SQL 文件,目标库执行即可。字段映射关注三件事:**字段名称**(中文注释匹配/人工填写)、 **字段类型**(自动转换)、**额外字段**(目标表必填、源表没有的列)。 - 实现语言:Java 8+(仅 JDK 标准库,零第三方依赖) - 驱动 jar:`lib\` 下已备好(mysql-connector-j 8.0.30 + postgresql 42.7.3),内网随项目拷贝即可 - 适用规模:单表 < 10 万行;目标表为空表 - 适配 Windows:`run.bat`/`build.bat` 已处理 GBK 控制台等兼容问题 ## 流程总览 ``` extract ──> [人工填写映射] ──> generate ──> [目标库执行 SQL] ──> verify(可选) 提取元数据+映射配置 处理待办清单 校验+生成 SQL psql/pgAdmin 校验报告 ``` - **extract(第一步)**:提取源表元数据 + 生成映射配置(目标库可连时按中文注释精确匹配预填 dst_col;连不上时手动模式,dst_col 全部留空)→ 待办清单(控制台输出) - **generate(第二步)**:校验映射 + 连接 MySQL 取数生成 SQL(`output/sql/*.sql`)→ 结果汇总(控制台输出) - **verify(可选)**:迁移后 count/抽样校验 ## 目录结构 ``` mysql2pg\ ├── lib\ # JDBC 驱动 jar(已就绪) ├── config\ # 人工配置 │ ├── db.properties # 数据库连接 + 全局选项(人工填) │ ├── tables.properties # 表清单:源表名=目标表名(人工填) │ ├── table_options.properties # 表级覆盖配置(可选,仅特殊表,如 extra_defaults) │ └── mapping\ # 每表一个字段映射 csv(extract 自动生成,人工确认) ├── work\ # 中间产物(可删) ├── output\ │ ├── sql\ # 生成的 PG SQL(每表一个文件) │ └── report\ # 校验结果 verification.csv ├── demo\ # 模拟数据 SQL(演练用) ├── src\ # 源码 ├── build.bat # 编译(run.bat 会自动调用) └── run.bat # 命令入口 ``` ## Web 页面(推荐,替代命令行 + Excel) ``` run.bat web [端口] :: 启动 Web 服务(默认 8080) ``` 浏览器访问 `http://localhost:8080`(内网机器用 `http://内网IP:8080`)。单页五个 Tab: | Tab | 功能 | |---|---| | 连接配置 | 图形化编辑 db.properties / tables.properties / table_options.properties(textarea 保存) | | 表与映射 | 运行 extract → 表状态列表(已映射/待填/忽略列)→ 点"编辑映射"进入映射编辑 | | 映射编辑 | 表格编辑映射(dst_col 输入框、convert 下拉、default 输入,替代 Excel);**候选列输入关键词实时模糊匹配**目标列(按中文注释/列名,点选即填入),保存后运行 generate | | 生成 SQL | 运行 generate → 日志滚动 → SQL 文件列表(点击预览) | | 校验 | 运行 verify → 校验结果表格 | **技术**:前后端不分离——JDK 内置 HttpServer 托管页面与 API,前端原生 JS(无框架、无 CDN), 零第三方依赖。**内网部署**:将 `classes\` + `lib\` + `static\` + `config\` 拷贝到内网机器 → `run.bat web` 启动即可。 ## 完整操作步骤(命令行) ### 第 1 步:配置(一次性) **① `config/db.properties`** —— 连接信息 + 全局选项: ```properties # ---- MySQL 源库 ---- mysql.host=127.0.0.1 mysql.port=3306 mysql.user=root mysql.password=root mysql.database=你的源库名 mysql.charset=utf8mb4 # ---- PostgreSQL 目标库(可连则自动匹配+校验;连不上自动降级手动映射) ---- pg.host=127.0.0.1 pg.port=5432 pg.user=postgres pg.password=123456 pg.database=你的目标库名 pg.schema=public # ---- 全局忽略源表 id 列(自增列) ---- ignore_identity=true # true = 提取与 INSERT 都不含 id 列,由 PG identity 自动生成(目标表全新系统、源 id 无业务意义时用,全表生效) # false = 保留源 id 原值显式插入(需目标表 GENERATED BY DEFAULT AS IDENTITY) ``` **② `config/tables.properties`** —— 17 张表清单,每行一张 `源表名=目标表名`(同名可留空): ```properties t_customer=customer t_order=order_info t_goods= ``` ### 第 2 步:提取元数据 + 生成映射(extract) ``` run.bat extract ``` 提取 MySQL 源表字段元数据(含中文注释),生成映射配置 `config/mapping/*.csv` 和待办清单(控制台输出)。 **两种模式自动切换**: | 模式 | 触发条件 | 字段映射方式 | |---|---|---| | 自动映射 | 目标库可连接 | 按中文注释精确匹配自动预填 dst_col(模糊/无匹配留空+候选) | | 手动映射 | 目标库连不上(5 秒超时判定) | 映射 CSV 模板源侧自动填好,**dst_col 全部留空**,人工对照目标表 DDL 填写 | 只处理部分表:`run.bat extract --table t_customer` / `run.bat extract --pattern "wqzb%"` (含 `%` 按 LIKE,否则按正则完整匹配)。 ### 第 3 步:处理待办清单(人工) 查看控制台输出的映射待办清单,处理待办项,然后运行 generate: 1. **dst_col 留空的源列**(模糊匹配/手动模式的列)→ 在 `config/mapping/{源表名}.csv` 的 dst_col 列填写目标列名(留空 = 跳过该源列) 2. **额外字段**(目标表必填但源表没有)→ 在 `config/table_options.properties` 配置 `{源表名}.extra_defaults`,格式 `列名:SQL表达式;列名:SQL表达式`: ```properties t_customer.extra_defaults=data_source:'migrated';sync_flag:0 ``` 3. **convert 类型核对**(自动按源类型推断,特殊列人工改): - `tinyint(1)` 状态码列(注释如"状态(0待付款1已付款…)")→ 改为 `int`(否则输出 TRUE/FALSE) - 源值为 NULL 且目标 NOT NULL → 在 `default` 列填替代值(`0`、`'unknown'`、`CURRENT_TIMESTAMP`) > 已确认过的映射文件不会被 extract 覆盖(重跑复用),`--force` 才重建。 ### 第 4 步:校验 + 生成 SQL(generate) ``` run.bat generate ``` 校验映射(目标列存在性、必填覆盖、convert 合法)→ 连接 MySQL 取数按映射逐行转换, 生成 `output/sql/{目标表名}.sql`(每表一个),结果汇总(控制台输出)。 校验/生成失败项不中断,统一进报告,处理完重跑 generate。 ### 第 5 步:目标库执行 SQL(人工) ```bat :: 方式一:psql psql -h 127.0.0.1 -U postgres -d 目标库 -v ON_ERROR_STOP=1 -f output\sql\customer.sql :: 方式二:pgAdmin / DBeaver 打开 SQL 文件执行(文件头 SET 已保证语义) ``` 建议先执行 1 张表抽查,确认无误再批量执行。 ### 第 6 步:校验(可选,需目标库可连) ``` run.bat verify ``` count 对比 + 抽样比对(有显式 id 时),结果写入 `output/report/verification.csv` 并在控制台输出。 忽略 id 模式(`ignore_identity=true`)下校验退化为 count 对比。 ## 映射配置格式 **字段映射 `config/mapping/{源表名}.csv`**(extract 生成,人工确认): ``` src_col,src_comment,src_type,dst_col,dst_comment,convert,default name,客户姓名,varchar(50),full_name,客户姓名,string, is_vip,是否VIP,tinyint(1),is_vip,是否VIP,bool_int,false ``` | 列 | 含义 | 人工操作 | |---|---|---| | src_col / src_comment / src_type | 源字段信息 | 只读(自动) | | dst_col | **目标字段名** | 自动模式精确匹配预填;其余人工填;**留空 = 跳过该源列** | | dst_comment | 目标注释/候选(`候选: amount(0.95); price(0.80)`) | 参考 | | convert | 类型转换方式 | 自动推断,特殊列人工改 | | default | 源 NULL 时替代 SQL 表达式 | 目标 NOT NULL 时填 | **convert 类型清单**: | convert | 适用源类型 | 输出示例 | |---|---|---| | identity | 自增列(AUTO_INCREMENT) | `1` | | pk | **非自增主键**(PRIMARY KEY 无自增,自动识别;不受 ignore_identity 影响,值必须迁移) | `'P001'` / `42` | | int / big_unsigned | 整数(含 unsigned) | `42` | | decimal / float / double | 数值 | `100.50` | | bool_int | TINYINT(1)/BIT(1) 布尔 | `TRUE` / `FALSE` | | string | CHAR/VARCHAR/TEXT/ENUM/SET | `'文本'` | | datetime / date | DATETIME/TIMESTAMP/DATE | `'2026-01-15 09:30:00'`(零日期自动转 NULL) | | json | JSON | `'{"a":1}'` | | bytes | BLOB/BINARY | `'\x89504e...'` | **表级覆盖配置 `config/table_options.properties`**(可选,没有特殊配置的表不用写任何条目): ```properties # 键格式: {源表名}.{配置项}=值 t_customer.extra_defaults=data_source:'migrated' # ★ 额外字段:目标表必填但源表没有的列 t_goods.pre_action=truncate # 目标表非空时先清空(默认 error_if_not_empty) t_order.batch_rows=1000 # 每条 INSERT 行数(默认 500) ``` 表名/目标表名映射(tables.properties)、schema(db.properties 的 pg.schema)、 id 列处理(db.properties 的 ignore_identity)、主键识别均自动处理,无需在此重复配置。 **主键识别规则**(提取源表元数据时从 `information_schema.KEY_COLUMN_USAGE` 识别 PRIMARY KEY): - 自增主键(AUTO_INCREMENT)→ convert=`identity`:`ignore_identity=true` 时跳过由 PG 生成 - **非自增主键**(无 AUTO_INCREMENT,如业务主键/手动赋值)→ convert=`pk`: **不受 ignore_identity 影响,主键值原样迁移**,并用于 verify 抽样比对定位 - 无主键表:verify 退化为仅 count 对比 ## MySQL → PG 转换规则(自动处理) | 场景 | 处理 | |---|---| | 标识符 | 反引号 → 双引号(`"public"."customer"`) | | 字符串转义 | `'` → `''`(反斜杠原样,standard_conforming_strings=on) | | NUL 字节(\0) | 剔除并告警(PG 文本列不支持) | | TINYINT(1)/BIT(1) 布尔 | `0/1` → `FALSE/TRUE` | | BIGINT UNSIGNED | 数值直接输出(目标建 NUMERIC(20)) | | DECIMAL | 无损输出(不用 float) | | 零日期 0000-00-00 | → NULL 并告警(PG 不支持) | | ENUM/SET | 字符串直接搬(目标建议 VARCHAR+CHECK) | | JSON → JSONB | 写出前校验 | | BLOB → BYTEA | `'\x89504e470d0a1a0a'` 十六进制字面量 | | 自增列 | ignore_identity=true 时跳过(PG 生成);false 时显式插原值 + setval 同步序列 | ## 演练环境(demo) ```bat :: MySQL 源库(重建 migrate_src + 3 张表 + 模拟数据) mysql -uroot -p < demo\mysql_source.sql :: PG 目标库(重建 migrate_dst + 3 张目标表 + 字段中文注释) psql -h 127.0.0.1 -U postgres -f demo\pg_target.sql ``` 覆盖全部转换特性:自增/中文注释/枚举/布尔/状态码/零日期/小数秒/NUL/JSON/BLOB/额外字段。 ## 常用命令 ```bat run.bat extract :: 第一步(全部表) run.bat extract --table t_customer :: 单表 run.bat extract --pattern "wqzb%" :: LIKE 筛选(wqzb 开头) run.bat extract --pattern "^t_(a|b)$" :: 正则筛选 run.bat extract --force :: 重建已确认过的映射文件 run.bat generate :: 第二步:校验 + 生成 SQL run.bat generate --table t_customer :: 单表 run.bat verify :: 迁移后校验(需目标库可连) run.bat verify --table t_customer :: 单表校验 ``` ## 常见问题 | 现象 | 原因与处理 | |---|---| | extract 报告"待人工填写 N 列" | 模糊匹配/手动模式的列,在映射 csv 填 dst_col 后运行 generate | | generate 报告"目标表必填列 xxx 未被映射" | 在 `config/table_options.properties` 配 `{源表名}.extra_defaults=列名:SQL表达式` | | 执行 SQL 报 boolean 类型错误 | tinyint(1) 状态码列,把 convert 改为 `int` 后重跑 | | generate 报告"目标表已有数据" | 幂等保护。确认可清空后改 `pre_action=truncate` 再生成 | | 目标库连不上 | 自动降级手动映射:dst_col 全部人工填写,校验/verify 相应跳过 | | 中文乱码 | CSV 为 utf-8-sig(Excel 直接打开);SQL 为 UTF-8 无 BOM |