【Python 小工具】一键把源表 INSERT SQL 转换成目标表 INSERT SQL

背景

在微服务/多租户系统里,经常会出现两张结构相近但用途不同的表:

  • SRC_T(运行期完整数据,含租户字段 TENANT_COL
  • DST_T(初始化模板表,无租户字段,字段顺序也不同)

当运营同事在测试环境通过 UI 配置完一整套数据后,需要把生成的 SRC_T INSERT 语句“迁移”到 DST_T,供初始化脚本复用。
手动改字段非常痛苦,于是写了一个小脚本,一条命令搞定。


两张表的区别

字段SRC_TDST_T
TENANT_COL
STATUS_COL
FLAG_A1/2统一 2
FLAG_B0/1统一 1
FLAG_C0/1统一 0
字段顺序24 列22 列

脚本功能

  • 读取任意 .sql 文件,自动提取所有 INSERT INTO … SRC_T … 语句。
  • 自动补齐缺失字段,并重排顺序。
  • CODE_COL 为空时,用 ID_COL 的 MD5 生成 32 位唯一值。
  • 生成可直接执行的 dst.sql

源码(单文件,零依赖)

复制即可运行,Python 3.7+

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
convert_generic.py
把 SRC_T 的 INSERT SQL 转成 DST_T 的 INSERT SQL
"""

import re
import hashlib
from pathlib import Path

# 目标表字段顺序(按业务需要调整)
TARGET_COLS = [
    'COL_01', 'COL_02', 'COL_03', 'COL_04', 'COL_05', 'COL_06', 'COL_07',
    'COL_08', 'COL_09', 'COL_10', 'COL_11', 'COL_12', 'COL_13', 'COL_14',
    'COL_15', 'COL_16', 'COL_17', 'COL_18', 'COL_19', 'COL_20', 'COL_21',
    'COL_22'
]

# 统一写死的默认值
DEFAULTS = {'COL_15': 2, 'COL_16': 1, 'COL_17': 0}

def md5_32(s: str) -> str:
    return hashlib.md5(s.encode()).hexdigest()

def parse_insert(line: str) -> dict:
    line = line.strip().rstrip(';')
    m = re.search(r'VALUES\s*\((.+)\)', line, re.S | re.I)
    if not m:
        return None
    values = [v.strip().strip("'\"") or None
              for v in re.split(r''',(?=(?:[^'"]|'[^']*'|"[^"]*")*$)''', m.group(1))]
    # 源表字段顺序(按实际调整)
    SRC_COLS = [
        'COL_01', 'COL_02', 'COL_03', 'COL_04', 'COL_05', 'COL_06', 'COL_07',
        'COL_08', 'COL_09', 'COL_10', 'COL_11', 'COL_12', 'COL_13', 'COL_14',
        'STATUS_COL', 'FLAG_C', 'FLAG_A', 'COL_18', 'COL_19', 'COL_20',
        'COL_21', 'COL_22', 'COL_23', 'COL_24', 'TENANT_COL', 'FLAG_B'
    ]
    record = dict(zip(SRC_COLS, values))
    # 类型转换示例
    for k in ['COL_01', 'COL_04', 'COL_05', 'COL_18', 'COL_20']:
        if str(record.get(k, '')).lstrip('-').isdigit():
            record[k] = int(record[k])
    record['COL_11'] = str(record.get('COL_11', '0'))
    # 空 CODE 自动生成
    if not record.get('COL_02'):
        record['COL_02'] = md5_32(str(record['COL_01']))
    return record

def convert(src_file: Path, dst_file: Path):
    text = src_file.read_text(encoding='utf-8')
    inserts = re.findall(
        r'INSERT\s+INTO\s+(?:`?\w+`?\.)?`?SRC_T`?\s*\([^)]*\)\s*VALUES\s*\([^)]*\);',
        text, re.I | re.S
    )
    records = [parse_insert(i) for i in inserts if parse_insert(i)]

    sql_lines = []
    for rec in records:
        for k, v in DEFAULTS.items():
            rec.setdefault(k, v)
        values = []
        for col in TARGET_COLS:
            val = rec.get(col)
            if val is None or str(val).upper() == 'NULL':
                values.append('NULL')
            elif isinstance(val, int):
                values.append(str(val))
            else:
                values.append(f"'{str(val).replace(\"'\", \"''\")}'")
        cols_sql = ', '.join(f'`{c}`' for c in TARGET_COLS)
        vals_sql = ', '.join(values)
        sql_lines.append(f"INSERT INTO DST_T ({cols_sql}) VALUES ({vals_sql});")

    dst_file.write_text('\n'.join(sql_lines), encoding='utf-8')
    print(f'✅ 已生成 {dst_file},共 {len(sql_lines)} 条记录。')

if __name__ == '__main__':
    import sys
    src_path = Path(sys.argv[1]) if len(sys.argv) > 1 else Path('input.sql')
    dst_path = src_path.with_name('dst.sql')
    convert(src_path, dst_path)

使用方法

  1. 把脚本保存为 convert_generic.py
  2. 把需要转换的 SQL 文件(例如 input.sql)放到相同目录。
  3. 打开终端:
python convert_generic.py input.sql
  1. 目录下会生成 dst.sql,直接导入即可:
mysql -h127.0.0.1 -uroot -p target_db < dst.sql

效果示例

输入(SRC_T 片段)

INSERT INTO `db_placeholder`.`SRC_T`
(`COL_01`, `COL_02`, `COL_03`, `COL_04`, `COL_05`, `COL_06`, ...)
VALUES
(1234567890123456000, 'abc123', '示例菜单', 0, 1, 'path_demo', ...);

输出(DST_T)

INSERT INTO DST_T
(`COL_01`, `COL_02`, `COL_03`, `COL_04`, `COL_05`, `COL_06`, ...)
VALUES
(1234567890123456000,'abc123','示例菜单',0,1,'path_demo',...);

小结

  • 纯正则解析,不依赖数据库客户端。
  • 支持任意库名前缀(db.SRC_T)。
  • 一键生成,再也不用手工改字段啦!

如需扩展(增删列、过滤条件等),直接改 TARGET_COLSSRC_COLSDEFAULTS 字典即可。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值