当前位置: 首页 > news >正文

MySQL转PostgreSQL迁移实战:从语法错误到完美兼容

MySQL转PostgreSQL迁移实战:从语法错误到完美兼容

前言

在数据库迁移项目中,从MySQL转换到PostgreSQL是一个常见但充满挑战的任务。最近我在一个项目中遇到了这样的需求,在转换过程中遇到了各种语法错误和兼容性问题。本文将详细记录整个修复过程,希望能为遇到类似问题的开发者提供参考。

问题背景

项目需要将现有的MySQL数据库迁移到PostgreSQL,原始SQL文件包含了大量的表结构定义、索引、约束和数据。在直接执行转换后的SQL文件时,遇到了各种语法错误,需要逐一修复。

遇到的主要问题及解决方案

1. 时间戳语法差异

问题描述:

-- MySQL语法
time_stamp_ NULL NOT NULL DEFAULT CURRENT_TIMESTAMP(3)

错误信息:

错误: 语法错误 在 "None" 或附近的
LINE 8: time_stamp_ NULL NOT NULL DEFAULT CURRENT_TIMESTAMP(3),

问题分析:
PostgreSQL不支持CURRENT_TIMESTAMP(3)这种带精度参数的时间戳语法,同时字段名中包含NULL也会导致语法冲突。

解决方案:

-- PostgreSQL语法
"time_stamp_NULL" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP

修复代码:

# 修复时间戳精度问题
content = re.sub(r'CURRENT_TIMESTAMP\(\d+\)', 'CURRENT_TIMESTAMP', content)
content = re.sub(r'(\w+_NULL)\s+', r'"\1" ', content)

2. 自增主键语法差异

问题描述:

-- MySQL语法
id bigint NOT NULL AUTO_INCREMENT

解决方案:

-- PostgreSQL语法
id bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY

修复代码:

content = re.sub(r'bigint NOT NULL AUTO_INCREMENT', 'bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY', content)

3. 数据类型映射问题

MySQL和PostgreSQL在数据类型上有很大差异,需要进行映射转换:

MySQL类型PostgreSQL类型说明
tinyintint216位整数
smallintint216位整数
intint432位整数
bigintint864位整数
datetimetimestamp时间戳
texttext文本类型
blobbytea二进制数据
bitbit位类型
booleanboolean布尔类型

修复代码:

type_mappings = {r'\btinyint\b': 'int2',r'\bsmallint\b': 'int2',r'\bint\b': 'int4',r'\bbigint\b': 'int8',r'\bdatetime\b': 'timestamp',r'\bblob\b': 'bytea',# ... 更多映射
}for mysql_type, pg_type in type_mappings.items():content = re.sub(mysql_type, pg_type, content, flags=re.IGNORECASE)

4. 字段名和保留字冲突

问题描述:

-- 错误语法
DROP "table" "if" EXISTS dual

错误信息:

错误: 语法错误 在 ""table"" 或附近的
LINE 14: DROP "table" "if" EXISTS dual

问题分析:
自动转换工具过于激进,将SQL关键字也用双引号包围了,导致语法错误。

解决方案:

-- 正确语法
DROP TABLE IF EXISTS dual

修复代码:

# 修复被错误引用的SQL关键字
content = re.sub(r'DROP "table"', 'DROP TABLE', content)
content = re.sub(r'DROP "if"', 'DROP IF', content)
content = re.sub(r'"if" EXISTS', 'IF EXISTS', content)
content = re.sub(r'CREATE "table"', 'CREATE TABLE', content)

5. 缺失列问题

某些表缺少必要的列,导致应用运行时出错:

问题表及缺失列:

  • sys_coffee 表缺少 deletedtenant_id
  • erp_product 表缺少 brand_idskus
  • erp_product_brand 表缺少 brand_category
  • erp_product_reagent 表缺少 skus

修复代码:

missing_columns = {'sys_coffee': ['deleted int2 DEFAULT 0', 'tenant_id int4'],'erp_product': ['brand_id bigint', 'skus text'],'erp_product_brand': ['brand_category varchar(255)'],'erp_product_reagent': ['skus text']
}for table_name, columns in missing_columns.items():pattern = rf'(CREATE TABLE {table_name} \([\s\S]*?)(PRIMARY KEY \(id\));'for column in columns:replacement = rf'\1  {column},\n\2;'content = re.sub(pattern, replacement, content)

6. 布尔值语法差异

问题描述:

-- MySQL语法
deleted bit(1) DEFAULT b'0'

解决方案:

-- PostgreSQL语法
deleted boolean DEFAULT '0'

修复代码:

content = re.sub(r"b'0'", "'0'", content)
content = re.sub(r"b'1'", "'1'", content)
content = re.sub(r"DEFAULT b'0'", "DEFAULT '0'", content)
content = re.sub(r"DEFAULT b'1'", "DEFAULT '1'", content)

7. 字符集和存储引擎语法

问题描述:

-- MySQL语法
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '表注释' ROW_FORMAT = DYNAMIC;

解决方案:

-- PostgreSQL语法
);

修复代码:

content = re.sub(r'ENGINE = \w+.*?ROW_FORMAT = \w+;', ';', content)
content = re.sub(r'CHARACTER SET \w+ COLLATE \w+', '', content)

8. 序列管理

PostgreSQL需要显式创建序列来支持自增主键:

解决方案:

-- 为每个表创建序列
CREATE SEQUENCE IF NOT EXISTS table_name_id_seq;
ALTER TABLE table_name ALTER COLUMN id SET DEFAULT nextval('table_name_id_seq');
ALTER SEQUENCE table_name_id_seq OWNED BY table_name.id;

修复代码:

table_names = re.findall(r'CREATE TABLE (\w+)', content)
for table_name in table_names:if not table_name.startswith(('act_', 'flw_', 'qrtz_')):sequence_statements.append(f"""
CREATE SEQUENCE IF NOT EXISTS {table_name}_id_seq;
ALTER TABLE {table_name} ALTER COLUMN id SET DEFAULT nextval('{table_name}_id_seq');
ALTER SEQUENCE {table_name}_id_seq OWNED BY {table_name}.id;
""")

修复脚本架构

为了系统性地解决这些问题,我创建了一套修复脚本:

1. 基础修复脚本 (fix_postgres_sql.py)

  • 处理基本的语法转换
  • 添加缺失的列
  • 数据类型映射

2. 全面修复脚本 (comprehensive_fix.py)

  • 包含所有基础修复功能
  • 添加序列创建
  • 处理更多语法问题

3. 时间戳修复脚本 (fix_timestamp.py)

  • 专门修复时间戳相关问题
  • 处理精度参数问题
  • 修复字段名冲突

4. 字段名修复脚本 (fix_field_names.py)

  • 处理字段名和保留字冲突
  • 修复被错误引用的标识符

5. 特定问题修复脚本 (fix_specific_issues.py)

  • 修复具体的语法错误
  • 处理SQL关键字引用问题

6. 验证脚本 (verify_fix.py)

  • 验证修复效果
  • 检查遗留问题
  • 生成修复报告

7. 完整修复脚本 (complete_fix.py)

  • 整合所有修复功能
  • 按步骤执行修复
  • 提供详细进度报告

使用示例

# 快速修复(推荐)
python complete_fix.py input.sql output.sql# 分步修复
python fix_postgres_sql.py input.sql output1.sql
python comprehensive_fix.py output1.sql output2.sql
python fix_timestamp.py output2.sql output3.sql
python verify_fix.py output3.sql

修复统计

经过完整的修复过程,我们成功解决了:

  • 时间戳精度问题 - 修复了CURRENT_TIMESTAMP(3)等语法
  • 缺失列问题 - 为相关表添加了必要的列
  • 数据类型映射 - 完成了所有MySQL到PostgreSQL的类型转换
  • 序列创建 - 创建了133个序列支持自增主键
  • 语法兼容性 - 清理了所有MySQL特有的语法
  • 字段名冲突 - 解决了保留字和字段名冲突问题

最终生成的PostgreSQL SQL文件大小约30.9MB,包含了完整的表结构、索引、约束和数据。

经验总结

1. 自动化工具的局限性

虽然有很多MySQL到PostgreSQL的转换工具,但它们往往无法处理所有边界情况,特别是:

  • 复杂的字段名和保留字冲突
  • 应用特定的列缺失
  • 某些MySQL特有的语法

2. 分步骤修复的重要性

将修复过程分解为多个步骤,每个步骤专注于特定类型的问题,这样更容易:

  • 定位和解决问题
  • 验证修复效果
  • 回滚错误的修改

3. 验证的必要性

创建验证脚本非常重要,它能够:

  • 自动检查修复效果
  • 发现遗漏的问题
  • 提供修复报告

4. 备份和版本控制

在整个修复过程中,保持原始文件的备份和中间版本非常重要,这样可以在出现问题时快速回滚。

5. 测试环境验证

修复后的SQL文件应该在测试环境中充分验证,确保:

  • 语法正确性
  • 数据完整性
  • 应用兼容性

常见问题FAQ

Q: 修复后仍有语法错误怎么办?

A: 检查具体的错误信息,可能需要手动调整某些特定的语法。建议查看PostgreSQL官方文档了解具体的语法要求。

Q: 某些表的数据丢失了怎么办?

A: 检查INSERT语句是否正确转换,可能需要手动修复数据插入语句。特别注意数据类型和格式的转换。

Q: 序列不工作怎么办?

A: 确保序列创建语句在表创建之后执行,并且序列名称正确。检查序列是否与表正确关联。

Q: 性能问题如何解决?

A: PostgreSQL和MySQL在索引、查询优化等方面有差异,可能需要根据PostgreSQL的特点调整索引策略和查询语句。

结语

MySQL到PostgreSQL的迁移是一个复杂的过程,需要仔细处理各种语法差异和兼容性问题。通过系统性的修复脚本和验证机制,我们可以大大减少手动修复的工作量,提高迁移的成功率。

希望本文的经验和脚本能够帮助遇到类似问题的开发者。如果你在迁移过程中遇到其他问题,欢迎分享和交流。


http://www.lryc.cn/news/606183.html

相关文章:

  • AI学习笔记三十三:基于Opencv的单目标跟踪
  • vue3 v-html绑定数据,点击sub实现popover效果
  • STM32 USB 设备中间件 tinyusb
  • 超宽带测距+测角+无线通信一体化模组:智能门锁、智能遥控器、AR头戴、智能穿戴
  • 融媒体中心网络安全应急预案(通用技术框架)
  • Vmvare虚拟机的网络不可达问题
  • Spring Boot 异常处理:从全局捕获到优化用户体验!
  • 爱心烟花浪漫立方体轮播图 - 用代码表达爱意
  • 为Github Copilot创建自定义指令/说明/注意事项
  • 决策树实现回归任务
  • 基于Spring Boot实现中医医学处方管理实践
  • 【Agent,智能,workflow】
  • 【RH134 问答题】第 13 章 运行容器
  • uvicorn 启动重复加载 多次加载
  • [12月考试] B
  • Python 数据科学与可视化工具箱 (一) - 数组属性:`shape`, `dtype`, `ndim`, `size`
  • day28_2025-07-31
  • Valgrind终极指南:深入内存安全与性能瓶颈检测
  • 进程控制:从创建到终结的完整指南
  • 解决音视频开发中 因mp4中断 无法播放的问题
  • [特殊字符] 数据可视化结合 three.js:让 3D 呈现更精准,3 个优化经验谈
  • 前端框架Vue3(三)——路由和pinia
  • RabbitMQ安装与介绍
  • Disruptor高性能基石:Sequence并发优化解析
  • 去重、top_n()、pull()、格式化
  • 数据结构第4问:什么是栈?
  • BR/EDR PHY帧结构及其具体内容
  • 51c自动驾驶~合集12
  • python基础语法3,组合数据类型(简单易上手的python语法教学)(课后习题)
  • 从0到1了解热部署