最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

從MySQL轉(zhuǎn)換到PostgreSQL的遷移過(guò)程

 更新時(shí)間:2026年04月08日 09:41:59   作者:阿里小阿希  
在數(shù)據(jù)庫(kù)遷移項(xiàng)目中,從MySQL轉(zhuǎn)換到PostgreSQL是一個(gè)常見(jiàn)但充滿(mǎn)挑戰(zhàn)的任務(wù),最近我在一個(gè)項(xiàng)目中遇到了這樣的需求,在轉(zhuǎn)換過(guò)程中遇到了各種語(yǔ)法錯(cuò)誤和兼容性問(wèn)題,本文將詳細(xì)記錄整個(gè)修復(fù)過(guò)程,希望能為遇到類(lèi)似問(wèn)題的開(kāi)發(fā)者提供參考,需要的朋友可以參考下

前言

在數(shù)據(jù)庫(kù)遷移項(xiàng)目中,從 MySQL 轉(zhuǎn)換到 PostgreSQL 是一個(gè)常見(jiàn)但充滿(mǎn)挑戰(zhàn)的任務(wù)。兩者在數(shù)據(jù)類(lèi)型、函數(shù)實(shí)現(xiàn)、語(yǔ)法規(guī)范等方面存在顯著差異,直接執(zhí)行轉(zhuǎn)換后的 SQL 文件幾乎必然會(huì)遇到大量語(yǔ)法錯(cuò)誤。

本文基于一個(gè)真實(shí)的企業(yè)級(jí)遷移項(xiàng)目,系統(tǒng)記錄了從 MySQL 到 PostgreSQL 遷移過(guò)程中遇到的各類(lèi)兼容性問(wèn)題,并提供了完整的修復(fù)方案和自動(dòng)化腳本。希望能為從事類(lèi)似工作的數(shù)據(jù)庫(kù)管理員和開(kāi)發(fā)者提供切實(shí)可行的參考。

一、問(wèn)題背景

1.1 項(xiàng)目概況

  • 源數(shù)據(jù)庫(kù):MySQL 5.7+
  • 目標(biāo)數(shù)據(jù)庫(kù):PostgreSQL 13+
  • 數(shù)據(jù)規(guī)模:約 30.9MB 的 SQL 文件,包含完整的表結(jié)構(gòu)、索引、約束和數(shù)據(jù)
  • 涉及表數(shù)量:133+ 張表

1.2 核心挑戰(zhàn)

原始 SQL 文件在 PostgreSQL 環(huán)境中執(zhí)行時(shí),遇到了以下幾類(lèi)主要錯(cuò)誤:

錯(cuò)誤類(lèi)型典型表現(xiàn)影響范圍
數(shù)據(jù)類(lèi)型不兼容tinyint、datetime 等類(lèi)型無(wú)法識(shí)別幾乎所有表
時(shí)間戳語(yǔ)法差異CURRENT_TIMESTAMP(3) 報(bào)語(yǔ)法錯(cuò)誤含時(shí)間字段的表
自增主鍵語(yǔ)法AUTO_INCREMENT 不被識(shí)別所有含自增主鍵的表
保留字沖突字段名為 NULL、table 等關(guān)鍵字特定表
存儲(chǔ)引擎/字符集ENGINE=InnoDB 等 MySQL 特有語(yǔ)法表定義部分
布爾值字面量b'0'、b'1' 二進(jìn)制表示法布爾類(lèi)型字段
缺失列應(yīng)用運(yùn)行時(shí)發(fā)現(xiàn)字段不存在部分業(yè)務(wù)表

二、核心差異對(duì)照:MySQL vs PostgreSQL

在開(kāi)始修復(fù)之前,先明確兩者在關(guān)鍵語(yǔ)法上的差異:

功能點(diǎn)MySQL 語(yǔ)法PostgreSQL 語(yǔ)法
自增主鍵AUTO_INCREMENTGENERATED BY DEFAULT AS IDENTITY 或 SERIAL
當(dāng)前時(shí)間戳(無(wú)精度)CURRENT_TIMESTAMPCURRENT_TIMESTAMP
當(dāng)前時(shí)間戳(帶精度)CURRENT_TIMESTAMP(3)不支持,需去除精度參數(shù)
布爾值字面量b'0' / b'1''0' / '1' 或 false / true
類(lèi)型:tinyint1 字節(jié)整數(shù)int2(或 smallint
類(lèi)型:datetime日期時(shí)間timestamp
類(lèi)型:blob二進(jìn)制大對(duì)象bytea
存儲(chǔ)引擎ENGINE=InnoDB不支持,需刪除
字符集CHARACTER SET utf8mb4不支持,需刪除
注釋語(yǔ)法COMMENT 'xxx' 在列定義后COMMENT ON COLUMN 獨(dú)立語(yǔ)句

三、問(wèn)題分類(lèi)與解決方案

3.1 時(shí)間戳語(yǔ)法差異

問(wèn)題描述

MySQL 支持帶精度參數(shù)的時(shí)間戳定義:

-- MySQL
`time_stamp_` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP(3)

PostgreSQL 的 CURRENT_TIMESTAMP 不接受精度參數(shù),且字段名中的 NULL 會(huì)與關(guān)鍵字沖突。

錯(cuò)誤信息

錯(cuò)誤: 語(yǔ)法錯(cuò)誤 在 "NULL" 或附近的
LINE 8: `time_stamp_` NULL NOT NULL DEFAULT CURRENT_TIMESTAMP(3)

解決方案

import re
def fix_timestamp_issues(content):
    # 1. 移除 CURRENT_TIMESTAMP 的精度參數(shù)
    content = re.sub(r'CURRENT_TIMESTAMP\(\d+\)', 'CURRENT_TIMESTAMP', content)
    # 2. 修復(fù)字段名中的 NULL 關(guān)鍵字沖突(用雙引號(hào)包裹)
    content = re.sub(r'(\w+_NULL)\s+', r'"\1" ', content)
    return content

3.2 自增主鍵語(yǔ)法差異

問(wèn)題描述

MySQL 使用 AUTO_INCREMENT,PostgreSQL 需要替換為 GENERATED BY DEFAULT AS IDENTITY 或 SERIAL。

解決方案

def fix_auto_increment(content):
    # 方案一:使用 IDENTITY(PostgreSQL 10+ 推薦)
    content = re.sub(
        r'bigint NOT NULL AUTO_INCREMENT',
        'bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY',
        content
    )
    # 方案二:使用 SERIAL 簡(jiǎn)寫(xiě)(兼容舊版本)
    # content = re.sub(r'int\(11\) NOT NULL AUTO_INCREMENT', 'SERIAL', content)
    return content

注意:SERIAL 是語(yǔ)法糖,實(shí)際創(chuàng)建的是序列。IDENTITY 更符合 SQL 標(biāo)準(zhǔn),推薦使用。

3.3 數(shù)據(jù)類(lèi)型映射

完整映射表

MySQL 類(lèi)型PostgreSQL 類(lèi)型說(shuō)明
tinyintint216 位整數(shù)
smallintint216 位整數(shù)
mediumintint432 位整數(shù)
int / integerint432 位整數(shù)
bigintint864 位整數(shù)
floatfloat4單精度浮點(diǎn)
doublefloat8雙精度浮點(diǎn)
decimal(p,s)numeric(p,s)精確數(shù)值
datetimetimestamp時(shí)間戳(無(wú)時(shí)區(qū))
timestamptimestamp同上
datedate日期
timetime時(shí)間
char(n)char(n)定長(zhǎng)字符串
varchar(n)varchar(n)變長(zhǎng)字符串
texttext文本
blob / tinyblob / mediumblob / longblobbytea二進(jìn)制數(shù)據(jù)
bit(n)bit(n)位串
boolean / boolboolean布爾值
enum('a','b')varchar + CHECK 約束需顯式轉(zhuǎn)換

實(shí)現(xiàn)代碼

def fix_data_types(content):
    type_mappings = {
        r'\btinyint\b': 'int2',
        r'\bsmallint\b': 'int2',
        r'\bmediumint\b': 'int4',
        r'\bint\b(?!4|8|eger)': 'int4',  # 避免匹配 int4/int8/integer
        r'\bbigint\b': 'int8',
        r'\bdatetime\b': 'timestamp',
        r'\bblob\b': 'bytea',
        r'\btinyblob\b': 'bytea',
        r'\bmediumblob\b': 'bytea',
        r'\blongblob\b': 'bytea',
        r'\bfloat\b': 'float4',
        r'\bdouble\b': 'float8',
    }
    for mysql_type, pg_type in type_mappings.items():
        content = re.sub(mysql_type, pg_type, content, flags=re.IGNORECASE)
    return content

3.4 保留字與字段名沖突

問(wèn)題描述

自動(dòng)轉(zhuǎn)換工具可能過(guò)度使用雙引號(hào),將 SQL 關(guān)鍵字也錯(cuò)誤地引用了:

-- 錯(cuò)誤:轉(zhuǎn)換后的 SQL
DROP "table" "if" EXISTS dual;
-- 正確:應(yīng)該是
DROP TABLE IF EXISTS dual;

解決方案

def fix_reserved_words(content):
    # 修復(fù)被錯(cuò)誤引用的 SQL 關(guān)鍵字
    fixes = [
        (r'DROP "table"', 'DROP TABLE'),
        (r'DROP "if"', 'DROP IF'),
        (r'"if" EXISTS', 'IF EXISTS'),
        (r'CREATE "table"', 'CREATE TABLE'),
        (r'"primary" KEY', 'PRIMARY KEY'),
        (r'"foreign" KEY', 'FOREIGN KEY'),
        (r'"not" NULL', 'NOT NULL'),
        (r'"default"', 'DEFAULT'),
    ]
    for pattern, replacement in fixes:
        content = re.sub(pattern, replacement, content, flags=re.IGNORECASE)
    return content

3.5 缺失列補(bǔ)充

問(wèn)題描述

某些業(yè)務(wù)表缺少應(yīng)用運(yùn)行所必需的列,需要在遷移時(shí)自動(dòng)補(bǔ)充。

解決方案

def add_missing_columns(content):
    missing_columns_config = {
        '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_config.items():
        # 匹配 CREATE TABLE 語(yǔ)句的主鍵結(jié)束位置
        pattern = rf'(CREATE TABLE {table_name} \([\s\S]*?)(PRIMARY KEY \(id\));'
        columns_def = '\n  ' + ',\n  '.join(columns) + ','
        replacement = rf'\1{columns_def}\n\2;'
        content = re.sub(pattern, replacement, content)
    return content

3.6 布爾值語(yǔ)法差異

問(wèn)題描述

MySQL 使用 b'0' / b'1' 表示二進(jìn)制字面量,PostgreSQL 不支持此語(yǔ)法。

解決方案

def fix_boolean_literals(content):
    # 替換二進(jìn)制字面量
    content = re.sub(r"b'0'", "'0'", content)
    content = re.sub(r"b'1'", "'1'", content)
    # 或者轉(zhuǎn)換為布爾值(如果字段類(lèi)型已是 boolean)
    # content = re.sub(r"b'0'", "false", content)
    # content = re.sub(r"b'1'", "true", content)
    return content

3.7 存儲(chǔ)引擎與字符集語(yǔ)法

問(wèn)題描述

MySQL 在表定義末尾包含存儲(chǔ)引擎、字符集、注釋等信息,PostgreSQL 不支持這些語(yǔ)法。

解決方案

def fix_mysql_specific_syntax(content):
    # 刪除 ENGINE、AUTO_INCREMENT、ROW_FORMAT 等
    content = re.sub(
        r'ENGINE\s*=\s*\w+.*?ROW_FORMAT\s*=\s*\w+;',
        ';',
        content,
        flags=re.IGNORECASE
    )
    # 刪除 CHARACTER SET / COLLATE 子句
    content = re.sub(
        r'CHARACTER\s+SET\s+\w+\s+COLLATE\s+\w+',
        '',
        content,
        flags=re.IGNORECASE
    )
    # 處理表注釋?zhuān)≒ostgreSQL 需要單獨(dú)處理)
    # MySQL: COMMENT 'xxx' → 需提取并轉(zhuǎn)換為 COMMENT ON TABLE
    return content

3.8 序列管理(自增主鍵的序列創(chuàng)建)

問(wèn)題描述

PostgreSQL 使用序列來(lái)實(shí)現(xiàn)自增主鍵。如果使用 SERIAL 類(lèi)型,序列會(huì)自動(dòng)創(chuàng)建;但如果使用 IDENTITY 方式,則需要顯式處理。

解決方案

def create_sequences_for_tables(content):
    # 提取所有 CREATE TABLE 的表名
    table_names = re.findall(r'CREATE TABLE (\w+)', content, re.IGNORECASE)
    # 排除系統(tǒng)表或特定前綴的表
    exclude_prefixes = ('act_', 'flw_', 'qrtz_')
    sequence_statements = []
    for table_name in table_names:
        if not table_name.startswith(exclude_prefixes):
            sequence_statements.append(f"""
-- 為表 {table_name} 創(chuàng)建序列
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;
""")
    # 將序列語(yǔ)句追加到文件末尾
    content += "\n\n-- 自動(dòng)生成的序列定義\n" + "\n".join(sequence_statements)
    return content

3.9 索引與約束的兼容性處理

問(wèn)題描述

MySQL 和 PostgreSQL 在索引語(yǔ)法上存在差異,特別是全文索引、前綴索引等。

解決方案

def fix_index_syntax(content):
    # 1. 移除 FULLTEXT 索引(需替換為 PostgreSQL 的 GIN 索引)
    # MySQL: FULLTEXT INDEX idx_name (col1, col2)
    # PostgreSQL: CREATE INDEX idx_name ON table_name USING GIN (to_tsvector('english', col1 || ' ' || col2))
    content = re.sub(
        r'FULLTEXT\s+INDEX\s+\w+\s*\([^)]+\)',
        '-- FULLTEXT index removed, needs manual conversion to GIN',
        content,
        flags=re.IGNORECASE
    )
    # 2. 處理前綴索引(PostgreSQL 不支持)
    # MySQL: INDEX idx_name (col(10))
    # 需要移除前綴長(zhǎng)度或替換為表達(dá)式索引
    return content

四、完整修復(fù)腳本架構(gòu)

4.1 腳本文件結(jié)構(gòu)

mysql_to_pg_migration/
├── fixers/
│   ├── __init__.py
│   ├── timestamp_fixer.py      # 時(shí)間戳修復(fù)
│   ├── type_fixer.py           # 數(shù)據(jù)類(lèi)型映射
│   ├── auto_increment_fixer.py # 自增主鍵修復(fù)
│   ├── reserved_words_fixer.py # 保留字修復(fù)
│   ├── column_fixer.py         # 缺失列補(bǔ)充
│   ├── boolean_fixer.py        # 布爾值修復(fù)
│   ├── mysql_syntax_fixer.py   # MySQL 特有語(yǔ)法清理
│   ├── sequence_fixer.py       # 序列管理
│   └── index_fixer.py          # 索引兼容性處理
├── complete_fix.py             # 主控腳本
├── verify_fix.py               # 驗(yàn)證腳本
└── config.yaml                 # 配置文件

4.2 主控腳本 complete_fix.py

#!/usr/bin/env python3
"""
MySQL 到 PostgreSQL SQL 文件完整修復(fù)工具
用法: python complete_fix.py input.sql output.sql
"""
import sys
import re
import argparse
from pathlib import Path
# 導(dǎo)入各個(gè)修復(fù)模塊
from fixers import (
    fix_timestamp_issues,
    fix_auto_increment,
    fix_data_types,
    fix_reserved_words,
    add_missing_columns,
    fix_boolean_literals,
    fix_mysql_specific_syntax,
    create_sequences_for_tables,
    fix_index_syntax
)
class SQLFixer:
    def __init__(self, input_path, output_path):
        self.input_path = Path(input_path)
        self.output_path = Path(output_path)
        self.stats = {
            'fixes_applied': 0,
            'tables_found': 0,
            'sequences_created': 0
        }
    def read_sql(self):
        with open(self.input_path, 'r', encoding='utf-8') as f:
            return f.read()
    def write_sql(self, content):
        with open(self.output_path, 'w', encoding='utf-8') as f:
            f.write(content)
        print(f"? 修復(fù)完成,輸出文件: {self.output_path}")
    def apply_fixes(self, content):
        """按順序應(yīng)用所有修復(fù)規(guī)則"""
        print("?? 開(kāi)始修復(fù) SQL 文件...")
        # 1. 基礎(chǔ)語(yǔ)法修復(fù)
        content = fix_timestamp_issues(content)
        self.stats['fixes_applied'] += 1
        print("  ? 時(shí)間戳語(yǔ)法修復(fù)完成")
        content = fix_auto_increment(content)
        self.stats['fixes_applied'] += 1
        print("  ? 自增主鍵修復(fù)完成")
        content = fix_data_types(content)
        self.stats['fixes_applied'] += 1
        print("  ? 數(shù)據(jù)類(lèi)型映射完成")
        content = fix_reserved_words(content)
        self.stats['fixes_applied'] += 1
        print("  ? 保留字沖突修復(fù)完成")
        content = add_missing_columns(content)
        self.stats['fixes_applied'] += 1
        print("  ? 缺失列補(bǔ)充完成")
        content = fix_boolean_literals(content)
        self.stats['fixes_applied'] += 1
        print("  ? 布爾值語(yǔ)法修復(fù)完成")
        content = fix_mysql_specific_syntax(content)
        self.stats['fixes_applied'] += 1
        print("  ? MySQL 特有語(yǔ)法清理完成")
        content = fix_index_syntax(content)
        self.stats['fixes_applied'] += 1
        print("  ? 索引語(yǔ)法兼容性處理完成")
        # 2. 序列管理(在表結(jié)構(gòu)之后)
        content = create_sequences_for_tables(content)
        self.stats['fixes_applied'] += 1
        self.stats['sequences_created'] = content.count('CREATE SEQUENCE')
        print(f"  ? 序列創(chuàng)建完成(共 {self.stats['sequences_created']} 個(gè))")
        return content
    def run(self):
        print(f"?? 讀取文件: {self.input_path}")
        content = self.read_sql()
        original_size = len(content)
        content = self.apply_fixes(content)
        final_size = len(content)
        self.write_sql(content)
        # 打印統(tǒng)計(jì)信息
        print("\n?? 修復(fù)統(tǒng)計(jì):")
        print(f"  - 原始文件大小: {original_size / 1024:.2f} KB")
        print(f"  - 最終文件大小: {final_size / 1024:.2f} KB")
        print(f"  - 應(yīng)用修復(fù)規(guī)則: {self.stats['fixes_applied']}")
        print(f"  - 創(chuàng)建序列數(shù)量: {self.stats['sequences_created']}")
def main():
    parser = argparse.ArgumentParser(description='MySQL 到 PostgreSQL SQL 文件修復(fù)工具')
    parser.add_argument('input', help='輸入 SQL 文件路徑')
    parser.add_argument('output', help='輸出 SQL 文件路徑')
    args = parser.parse_args()
    fixer = SQLFixer(args.input, args.output)
    fixer.run()
if __name__ == '__main__':
    main()

4.3 驗(yàn)證腳本 verify_fix.py

#!/usr/bin/env python3
"""
驗(yàn)證修復(fù)后的 SQL 文件,檢測(cè)潛在問(wèn)題
"""
import re
import sys
from pathlib import Path
class SQLValidator:
    def __init__(self, sql_path):
        self.sql_path = Path(sql_path)
        self.content = self.read_sql()
        self.issues = []
    def read_sql(self):
        with open(self.sql_path, 'r', encoding='utf-8') as f:
            return f.read()
    def check_auto_increment(self):
        """檢查是否還有未轉(zhuǎn)換的 AUTO_INCREMENT"""
        matches = re.findall(r'AUTO_INCREMENT', self.content, re.IGNORECASE)
        if matches:
            self.issues.append(f"?? 發(fā)現(xiàn) {len(matches)} 處未轉(zhuǎn)換的 AUTO_INCREMENT")
    def check_timestamp_precision(self):
        """檢查是否還有帶精度的時(shí)間戳"""
        matches = re.findall(r'CURRENT_TIMESTAMP\(\d+\)', self.content)
        if matches:
            self.issues.append(f"?? 發(fā)現(xiàn) {len(matches)} 處帶精度的時(shí)間戳")
    def check_mysql_types(self):
        """檢查是否還有 MySQL 特有類(lèi)型"""
        mysql_types = ['tinyint', 'mediumint', 'datetime', 'blob']
        for typ in mysql_types:
            matches = re.findall(rf'\b{typ}\b', self.content, re.IGNORECASE)
            if matches:
                self.issues.append(f"?? 發(fā)現(xiàn) {len(matches)} 處未轉(zhuǎn)換的類(lèi)型 '{typ}'")
    def check_engine_clause(self):
        """檢查是否還有 ENGINE 子句"""
        matches = re.findall(r'ENGINE\s*=', self.content, re.IGNORECASE)
        if matches:
            self.issues.append(f"?? 發(fā)現(xiàn) {len(matches)} 處未清理的 ENGINE 子句")
    def check_binary_literals(self):
        """檢查是否還有 b'0'/b'1' 字面量"""
        matches = re.findall(r"b'[01]'", self.content)
        if matches:
            self.issues.append(f"?? 發(fā)現(xiàn) {len(matches)} 處未轉(zhuǎn)換的二進(jìn)制字面量")
    def run_all_checks(self):
        self.check_auto_increment()
        self.check_timestamp_precision()
        self.check_mysql_types()
        self.check_engine_clause()
        self.check_binary_literals()
    def report(self):
        print(f"\n?? 驗(yàn)證文件: {self.sql_path}")
        print("=" * 50)
        if not self.issues:
            print("? 未發(fā)現(xiàn)明顯問(wèn)題,文件可以提交測(cè)試")
        else:
            print(f"?? 發(fā)現(xiàn) {len(self.issues)} 個(gè)潛在問(wèn)題:\n")
            for issue in self.issues:
                print(f"  {issue}")
        return len(self.issues) == 0
def main():
    if len(sys.argv) != 2:
        print("用法: python verify_fix.py <sql_file>")
        sys.exit(1)
    validator = SQLValidator(sys.argv[1])
    validator.run_all_checks()
    success = validator.report()
    sys.exit(0 if success else 1)
if __name__ == '__main__':
    main()

五、使用指南

5.1 快速開(kāi)始

# 1. 克隆或創(chuàng)建腳本目錄
mkdir mysql_to_pg_migration
cd mysql_to_pg_migration
# 2. 運(yùn)行完整修復(fù)
python complete_fix.py original.sql fixed.sql
# 3. 驗(yàn)證修復(fù)結(jié)果
python verify_fix.py fixed.sql
# 4. 在 PostgreSQL 中執(zhí)行
psql -d target_db -f fixed.sql

5.2 分步執(zhí)行(調(diào)試模式)

# 逐步應(yīng)用修復(fù),便于定位問(wèn)題
python fix_timestamp.py input.sql step1.sql
python fix_type.py step1.sql step2.sql
python fix_auto_increment.py step2.sql step3.sql
python verify_fix.py step3.sql

六、修復(fù)統(tǒng)計(jì)(基于實(shí)際項(xiàng)目)

修復(fù)項(xiàng)修復(fù)數(shù)量說(shuō)明
時(shí)間戳精度問(wèn)題約 200+ 處移除 CURRENT_TIMESTAMP(n) 中的精度參數(shù)
數(shù)據(jù)類(lèi)型轉(zhuǎn)換約 500+ 處tinyint → int2,datetime → timestamp 等
自增主鍵轉(zhuǎn)換133 處AUTO_INCREMENT → IDENTITY
序列創(chuàng)建133 個(gè)為每個(gè)表創(chuàng)建獨(dú)立的序列
存儲(chǔ)引擎語(yǔ)法清理133 處移除 ENGINE=InnoDB 等
保留字沖突修復(fù)約 50 處修復(fù) NULLtable 等關(guān)鍵字沖突
缺失列補(bǔ)充6 列為 4 張表補(bǔ)充業(yè)務(wù)必需的列
布爾值字面量約 30 處b'0' → '0'

最終輸出:約 30.9MB 的 PostgreSQL 兼容 SQL 文件。

七、常見(jiàn)問(wèn)題 FAQ

Q1: 修復(fù)后仍有語(yǔ)法錯(cuò)誤怎么辦?

A: 按以下步驟排查:

  • 查看 PostgreSQL 錯(cuò)誤信息,定位具體的 SQL 語(yǔ)句
  • 對(duì)照本文的差異對(duì)照表,檢查是否有遺漏的轉(zhuǎn)換規(guī)則
  • 手動(dòng)修復(fù)該語(yǔ)句,并考慮將該模式添加到修復(fù)腳本中
  • 參考 PostgreSQL 官方文檔 確認(rèn)正確語(yǔ)法

Q2: 某些表的數(shù)據(jù)丟失了怎么辦?

A: 可能原因及解決方案:

  • INSERT 語(yǔ)句格式問(wèn)題:MySQL 的 INSERT IGNORE、INSERT ... ON DUPLICATE KEY UPDATE 需要轉(zhuǎn)換
  • 數(shù)據(jù)格式不兼容:日期格式、轉(zhuǎn)義字符等需要額外處理
  • 編碼問(wèn)題:確保源文件和目標(biāo)數(shù)據(jù)庫(kù)使用相同字符集(推薦 UTF-8)

Q3: 序列不工作怎么辦?

A: 檢查以下幾點(diǎn):

-- 1. 確認(rèn)序列存在
SELECT * FROM information_schema.sequences WHERE sequence_name LIKE '%_id_seq';
-- 2. 確認(rèn)默認(rèn)值已設(shè)置
\d table_name
-- 3. 手動(dòng)設(shè)置序列值(如果需要同步現(xiàn)有數(shù)據(jù))
SELECT setval('table_name_id_seq', (SELECT MAX(id) FROM table_name));

Q4: 修復(fù)后性能比 MySQL 差怎么辦?

A: PostgreSQL 和 MySQL 的優(yōu)化策略不同:

  • 索引類(lèi)型:PostgreSQL 支持更多索引類(lèi)型(BRIN、GIN、GiST),可根據(jù)查詢(xún)模式調(diào)整
  • 統(tǒng)計(jì)信息:執(zhí)行 ANALYZE 更新統(tǒng)計(jì)信息
  • 配置調(diào)優(yōu):調(diào)整 shared_buffers、work_mem 等參數(shù)
  • 查詢(xún)重寫(xiě):某些 MySQL 特有的優(yōu)化 hint 需要移除

Q5: 如何處理存儲(chǔ)過(guò)程和觸發(fā)器?

A: 存儲(chǔ)過(guò)程和觸發(fā)器的轉(zhuǎn)換是最復(fù)雜的部分:

  • 語(yǔ)法差異:MySQL 使用 BEGIN ... END,PostgreSQL 使用 $$ ... $$ 和 LANGUAGE plpgsql
  • 變量聲明:MySQL 的 DECLARE 位置不同
  • 錯(cuò)誤處理:MySQL 的 DECLARE CONTINUE HANDLER 需轉(zhuǎn)換為 BEGIN ... EXCEPTION ... END
  • 建議使用 pgloader 或 AWS SCT 這類(lèi)專(zhuān)業(yè)工具輔助轉(zhuǎn)換

八、最佳實(shí)踐總結(jié)

8.1 遷移前準(zhǔn)備

  • 分析原始 SQL:統(tǒng)計(jì)表數(shù)量、數(shù)據(jù)量、使用的 MySQL 特性
  • 制定遷移策略:一次性遷移 vs 分批遷移
  • 準(zhǔn)備回滾方案:保留原始 SQL 備份,確??煽焖倩赝?/li>

8.2 遷移過(guò)程

  • 分步驟執(zhí)行:先遷移表結(jié)構(gòu),再遷移數(shù)據(jù),最后遷移約束和索引
  • 自動(dòng)化優(yōu)先:使用正則表達(dá)式批量處理重復(fù)性問(wèn)題
  • 保留手動(dòng)空間:某些復(fù)雜場(chǎng)景(如存儲(chǔ)過(guò)程)需要手動(dòng)轉(zhuǎn)換

8.3 遷移后驗(yàn)證

  • 語(yǔ)法驗(yàn)證:在測(cè)試環(huán)境中執(zhí)行 SQL,確保無(wú)語(yǔ)法錯(cuò)誤
  • 數(shù)據(jù)完整性:對(duì)比源庫(kù)和目標(biāo)庫(kù)的行數(shù)、關(guān)鍵字段的校驗(yàn)和
  • 應(yīng)用兼容性:運(yùn)行應(yīng)用的核心功能,驗(yàn)證 CRUD 操作正常
  • 性能基準(zhǔn)測(cè)試:對(duì)比關(guān)鍵查詢(xún)的執(zhí)行計(jì)劃

8.4 工具推薦

工具用途適用場(chǎng)景
pgloader全量遷移自動(dòng)化遷移 MySQL → PostgreSQL
AWS DMS / SCT云遷移大規(guī)模企業(yè)級(jí)遷移
re2c + 自定義腳本語(yǔ)法轉(zhuǎn)換處理復(fù)雜、非標(biāo)準(zhǔn)的 SQL 文件
pgAdmin驗(yàn)證調(diào)試手動(dòng)執(zhí)行和調(diào)試轉(zhuǎn)換后的 SQL

結(jié)語(yǔ)

MySQL 到 PostgreSQL 的遷移是一個(gè)系統(tǒng)工程,涉及語(yǔ)法、數(shù)據(jù)類(lèi)型、函數(shù)、存儲(chǔ)過(guò)程等多個(gè)層面的適配。本文基于真實(shí)項(xiàng)目經(jīng)驗(yàn),系統(tǒng)梳理了遷移過(guò)程中的常見(jiàn)問(wèn)題和解決方案,并提供了一套可擴(kuò)展的自動(dòng)化修復(fù)腳本。

核心要點(diǎn)回顧:

  • 差異認(rèn)知:充分理解兩種數(shù)據(jù)庫(kù)在語(yǔ)法和功能上的差異
  • 自動(dòng)化優(yōu)先:用腳本批量處理可重復(fù)的轉(zhuǎn)換任務(wù)
  • 驗(yàn)證驅(qū)動(dòng):每個(gè)修復(fù)步驟后都要驗(yàn)證,及早發(fā)現(xiàn)問(wèn)題
  • 分步推進(jìn):結(jié)構(gòu) → 數(shù)據(jù) → 約束 → 應(yīng)用,降低風(fēng)險(xiǎn)

以上就是從MySQL轉(zhuǎn)換到PostgreSQL的遷移過(guò)程的詳細(xì)內(nèi)容,更多關(guān)于MySQL轉(zhuǎn)換到PostgreSQL的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Mysql 索引該如何設(shè)計(jì)與優(yōu)化

    Mysql 索引該如何設(shè)計(jì)與優(yōu)化

    這篇文章主要介紹了Mysql 索引該如何設(shè)計(jì)與優(yōu)化,幫助大家更好的理解和學(xué)習(xí)使用MySQL,感興趣的朋友可以了解下
    2021-03-03
  • MySQL聚簇索引、非聚簇索引、覆蓋索引詳解

    MySQL聚簇索引、非聚簇索引、覆蓋索引詳解

    這篇文章詳細(xì)介紹了聚簇索引、非聚簇索引和覆蓋索引的概念,并通過(guò)圖示和實(shí)例說(shuō)明了索引查找的過(guò)程和回表查詢(xún)的概念,同時(shí),文章也提到了覆蓋索引的優(yōu)點(diǎn)和弊端,并給出了適用場(chǎng)景
    2024-12-12
  • 一文整理新手最常犯的SQL高頻語(yǔ)法錯(cuò)誤和避坑合集

    一文整理新手最常犯的SQL高頻語(yǔ)法錯(cuò)誤和避坑合集

    本文整理了SQL新手常見(jiàn)的10類(lèi)語(yǔ)法錯(cuò)誤,包括基礎(chǔ)語(yǔ)法錯(cuò)誤,CRUD操作錯(cuò)誤,查詢(xún)類(lèi)錯(cuò)誤等,每個(gè)錯(cuò)誤都提供錯(cuò)誤示例、正確寫(xiě)法及避坑技巧,希望對(duì)大家有所幫助
    2026-05-05
  • mysql實(shí)現(xiàn)查詢(xún)最接近的記錄數(shù)據(jù)示例

    mysql實(shí)現(xiàn)查詢(xún)最接近的記錄數(shù)據(jù)示例

    這篇文章主要介紹了mysql實(shí)現(xiàn)查詢(xún)最接近的記錄數(shù)據(jù),涉及mysql查詢(xún)相關(guān)的時(shí)間轉(zhuǎn)換、排序等相關(guān)操作技巧,需要的朋友可以參考下
    2018-07-07
  • 深度解析MySQL 鎖機(jī)制:間隙鎖、Next-Key Lock 與幻讀防御

    深度解析MySQL 鎖機(jī)制:間隙鎖、Next-Key Lock 與幻讀防御

    本文深入探討了MySQL的鎖機(jī)制,特別是間隙鎖(GapLock)和臨鍵鎖(Next-KeyLock),本文將從"為什么要鎖間隙"出發(fā),一路追到"為什么是左開(kāi)右閉",把這套機(jī)制徹底講透,感興趣的朋友跟隨小編一起看看吧
    2026-03-03
  • RC級(jí)別下MySQL死鎖問(wèn)題的解決

    RC級(jí)別下MySQL死鎖問(wèn)題的解決

    本文主要介紹了RC級(jí)別下MySQL死鎖問(wèn)題的解決,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-03-03
  • MySql8 WITH RECURSIVE遞歸查詢(xún)父子集的方法

    MySql8 WITH RECURSIVE遞歸查詢(xún)父子集的方法

    這篇文章主要介紹了MySql8 WITH RECURSIVE遞歸查詢(xún)父子集的方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-12-12
  • MySQL?8.0自增變量的持久化問(wèn)題小結(jié)

    MySQL?8.0自增變量的持久化問(wèn)題小結(jié)

    MySQL5.7中自增主鍵在重啟后會(huì)重置,而MySQL8.0中通過(guò)重做日志持久化自增變量,避免重啟后主鍵沖突,本文介紹MySQL?8.0自增變量的持久化問(wèn)題小結(jié),感興趣的朋友一起看看吧
    2024-11-11
  • mysql常用命令以及小技巧

    mysql常用命令以及小技巧

    這篇文章主要分享的是mysql常用命令以及小技巧,概述清理二進(jìn)制日志、mysqldump不鎖表、mysql跳過(guò)空事務(wù)等相關(guān)資料展開(kāi)主題,需要的小伙伴可以參考一下,希望對(duì)你有所幫助
    2022-02-02
  • 一條慢SQL語(yǔ)句引發(fā)的改造之路

    一條慢SQL語(yǔ)句引發(fā)的改造之路

    這篇文章主要給大家介紹了關(guān)于一條慢SQL語(yǔ)句引發(fā)的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2022-03-03

最新評(píng)論

潜山县| 宣汉县| 文安县| 宁国市| 密云县| 化州市| 贵德县| 抚松县| 三穗县| 祁东县| 长治市| 卫辉市| 固原市| 海安县| 丰城市| 体育| 西吉县| 汉沽区| 宁德市| 团风县| 兴和县| 唐海县| 襄汾县| 香港| 温州市| 桐柏县| 潜江市| 仲巴县| 名山县| 青州市| 老河口市| 城口县| 徐州市| 开鲁县| 柳河县| 碌曲县| 项城市| 秭归县| 榆树市| 万盛区| 潼南县|