Python中SQLite执行ALTER TABLE时出现关联SELECT语句的KeyError问题
问题分析与解决
你的错误看似矛盾——ALTER TABLE语句触发了KeyError,但错误信息却指向更早的SELECT语句,根源在于你在多线程中共享了同一个SQLite连接,导致了未定义的内存/状态混乱。
SQLite连接本身并非线程安全,哪怕设置了check_same_thread=False,也只是关闭了线程安全检查,并没有让连接具备多线程安全能力。多个线程同时操作同一连接会引发各种诡异问题:比如SQL语句被混淆、变量值被意外覆盖,就像你遇到的情况——主线程中的row变量(表名)被线程中的SELECT语句内容污染,最终导致格式化ALTER TABLE语句时触发KeyError。
修复步骤
1. 线程操作使用独立连接
让每个线程创建自己的SQLite连接,不要共享主线程的连接:
def get_data(number2, table_name, dataset): column = "id" # 每个线程独立创建连接 thread_con = sl.connect(dataset, check_same_thread=False) try: creation_time = thread_con.execute( "SELECT FileCreationTime,{} FROM {} WHERE file_number = ?;".format(column, table_name), (int(number2),) ).fetchall() except Exception as e: print(column, table_name, int(number2), e) raise finally: thread_con.close() # 用完及时关闭 if len(creation_time) > 1: time_list = [item[0] for item in creation_time] id_list = [item[1] for item in creation_time] # 按时间排序后的id列表 sorted_ids = [x for _, x in sorted(zip(time_list, id_list))] # 用锁保护全局字典,避免竞态条件 with lock: time_1[str(number2)] = sorted_ids
同时在主线程中添加锁,保护多线程共享的time_1字典:
# 在定义time_1前初始化锁 lock = threading.Lock() time_1 = {}
启动线程时传入数据集路径:
t1 = threading.Thread(target=get_data, args=(a1, row, dataset))
2. 避免SQL注入,使用参数化查询
你的UPDATE语句直接格式化字符串,存在注入风险且易出错,改成参数化写法:
con.execute( "UPDATE {} SET duplicate = ? WHERE id = ?;".format(row), (i, n) )
注:表名无法用参数化,所以format(row)是安全的(表名来自sqlite_master,为可信值)。
3. 可选:改用单线程处理(简单安全)
如果数据量不是极大,直接去掉多线程逻辑,单线程处理更稳定,无需处理锁和多连接:
# 替换原多线程代码块 for number2 in tqdm(position[:50]): get_data(number2, row)
其他小问题修正
file_number是整数类型,用=代替LIKE更符合逻辑;- 你仅处理了
position[:50](前50个file_number),若需处理所有重复项,去掉切片即可。
内容的提问来源于stack exchange,提问作者Piotr Yartsev
相关产品推荐
相关产品推荐

