跳转至

后端-数据库-跨版本表结构不一致导致数据导入失败

问题现象

  • 从服务器导出 recommendation_scores 表后,用 mysql < dump.sql 导入本地时报错
  • 错误信息:ERROR 1054 (42S22): Unknown column 'llm_score' in 'field list'
  • 服务器和本地虽然都用 MySQL,但表结构版本不同

核心问题

  • 服务器运行 AI 推荐 v2 版本,recommendation_scores 表有 12 列(含 llm_score、 negative_penalty、topic_score、depth_score、utility_score、source_score、topic_label)
  • 本地已升级为 AI 推荐 v4 版本,同表只有 6 列(id、news_type、news_id、 final_score、score_reason、scored_at)
  • mysqldump --complete-insert 生成的 INSERT 语句包含服务器所有列名,导入时因缺列失败

关键差异列对比: | 服务器 (v2) | 本地 (v4) | |------------|----------| | llm_score | (删除) | | negative_penalty | (删除) | | topic_score | (删除) | | depth_score | (删除) | | utility_score | (删除) | | source_score | (删除) | | topic_label | (删除) | | (无) | score_reason |

解决方法

方案:仅导出公共列,生成自定义 INSERT 语句

  1. 用 SQL CONCAT 在服务器端直接生成只包含公共列的 INSERT:
ssh news "mysql -u root -p'xxx' news -e \
  \"SELECT CONCAT(
    'INSERT INTO recommendation_scores (news_type, news_id, final_score, scored_at) VALUES (',
    QUOTE(news_type), ', ',
    news_id, ', ',
    IFNULL(final_score, 'NULL'), ', ',
    QUOTE(scored_at), ');'
  ) FROM recommendation_scores WHERE final_score IS NOT NULL;\"" \
  2>/dev/null > insert.sql
  1. 去掉 mysql 输出的表头行(第 1 行是列名 CONCAT(...)):
tail -n +2 insert.sql > insert_clean.sql
  1. 导入本地:
# 导入前禁用外键检查以确保 TRUNCATE 能执行
mysql -u root -p'xxx' news -e "SET FOREIGN_KEY_CHECKS=0; TRUNCATE TABLE recommendation_scores; SET FOREIGN_KEY_CHECKS=1;"
mysql -u root -p'xxx' news < insert_clean.sql

下次防错措施

  1. 导入前先对比表结构

    # 服务器
    ssh news "mysql -u root -p'xxx' news -e 'DESCRIBE recommendation_scores;'" 2>/dev/null
    # 本地
    mysql -u root -p'xxx' news -e "DESCRIBE recommendation_scores;"
    

  2. 跨版本迁移时优先考虑脚本:不用 mysqldump 直接用,而是编写 Python 脚本做字段映射和转换

    # 示例:用 SQLAlchemy 做字段级映射导入
    from src.core.database import async_session_factory
    # 逐行读取源数据,按目标表结构映射后插入
    

  3. 模块版本变更时记录 Schema 差异:在模块升级文档中列出新增/删除/变更的列