You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何在Python中用recent_db更新同表结构的archive_db SQLite3数据库?

哦,我懂你遇到的问题了——用iterdump()生成的脚本会包含完整的建表语句,而你的archive_db里已经存在结构完全一致的表,执行时自然会触发「表已存在」的错误,而且这本质是全量导入,并不是你想要的「更新」逻辑。

下面给你两种常用的解决方案,根据你的实际需求选择:

方案1:全量替换(清空归档库表后插入新数据)

如果你想直接用recent_db的内容完全覆盖archive_db对应表的所有数据(不保留旧数据),可以通过附加数据库的方式在一个连接里操作两个库,步骤如下:

import sqlite3

archive_db = "archive_db"
recent_db = "recent_db"

# 连接归档数据库
conn = sqlite3.connect(archive_db)
cursor = conn.cursor()

# 附加recent_db为临时别名'recent',方便跨库操作
cursor.execute(f"ATTACH DATABASE '{recent_db}' AS recent;")

# 获取recent_db中所有用户表(排除SQLite系统表)
cursor.execute("SELECT name FROM recent.sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%';")
tables = [row[0] for row in cursor.fetchall()]

for table in tables:
    # 先清空归档库中的当前表
    cursor.execute(f"DELETE FROM {table};")
    # 从recent_db插入所有数据到归档库的对应表
    cursor.execute(f"INSERT INTO {table} SELECT * FROM recent.{table};")

# 提交更改并关闭连接
conn.commit()
conn.close()

方案2:增量更新(更新已有记录,插入新增记录)

如果你需要保留archive_db中未被recent_db覆盖的旧数据,仅更新两者共有的记录、插入recent_db的新增记录,这就需要用到SQLite的冲突处理语法(前提是你的表有主键或唯一约束):

方法A:使用INSERT OR REPLACE(简单直接)

这个语法会在主键冲突时替换整条记录,适合你希望用recent_db的最新数据完全覆盖旧记录的场景:

import sqlite3

archive_db = "archive_db"
recent_db = "recent_db"

conn = sqlite3.connect(archive_db)
cursor = conn.cursor()
cursor.execute(f"ATTACH DATABASE '{recent_db}' AS recent;")

# 获取所有用户表
cursor.execute("SELECT name FROM recent.sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%';")
tables = [row[0] for row in cursor.fetchall()]

for table in tables:
    # 先检查表是否有主键(无主键则无法进行增量更新,退化为全量替换)
    cursor.execute(f"PRAGMA recent.table_info({table});")
    column_info = cursor.fetchall()
    primary_keys = [col[1] for col in column_info if col[5] == 1]
    
    if not primary_keys:
        print(f"警告:表{table}无主键,将执行全量替换")
        cursor.execute(f"DELETE FROM {table};")
        cursor.execute(f"INSERT INTO {table} SELECT * FROM recent.{table};")
        continue
    
    # 执行插入或替换:主键存在则替换,不存在则插入
    cursor.execute(f"INSERT OR REPLACE INTO {table} SELECT * FROM recent.{table};")

conn.commit()
conn.close()

方法B:使用ON CONFLICT(更灵活)

如果你的SQLite版本在3.24.0及以上,可以用ON CONFLICT语法自定义更新字段,比如只更新部分字段而不是整条记录:

# 假设表的主键是`id`,需要更新的字段是`col1`、`col2`、`col3`
cursor.execute(f"""
INSERT INTO {table} SELECT * FROM recent.{table}
ON CONFLICT(id) DO UPDATE SET
    col1 = excluded.col1,
    col2 = excluded.col2,
    col3 = excluded.col3
""")

注意事项

  1. 确保两个数据库的表结构完全一致,否则插入/更新时会触发字段不匹配的错误;
  2. 如果表有外键约束,建议操作前关闭外键检查(操作后再开启):
    cursor.execute("PRAGMA foreign_keys = OFF;")
    # 执行更新操作...
    cursor.execute("PRAGMA foreign_keys = ON;")
    
  3. 操作前务必备份两个数据库,避免数据丢失!

内容的提问来源于stack exchange,提问作者user5805065

火山引擎 最新活动