psycopg2 execute_values在Supabase中处理超100行数据异常求助
核心原因推测
你的问题和psycopg2.extras.execute_values的默认行为直接相关:这个函数默认按每100条数据拆分一个批次执行SQL。当数据量超过100时,会自动分成多个批次处理,而你提前用mogrify结合AsIs构造SQL的方式,和execute_values的批量逻辑冲突,导致后续批次的语句出现异常,进而产生将csm_id设为NULL的错误插入。
具体问题点
你通过mogrify把VALUES %s替换成了AsIs("%s"),但execute_values本身会自动生成VALUES (...), (...), ...的批量格式。当拆分批次时,后续批次的SQL语句会因为提前的占位符替换,出现字段引用错误(比如EXCLUDED关键字的解析异常),最终导致部分字段被错误地设为NULL。
修正方案
1. 重构SQL语句构造逻辑
不要用mogrify提前处理整个SQL,改用psycopg2.sql模块安全地构造动态表名和字段,让execute_values负责处理批量VALUES部分:
from psycopg2 import sql from psycopg2.extras import execute_values # 安全构造动态SQL,避免SQL注入 statement = sql.SQL(''' INSERT INTO {} ({}) VALUES %s ON CONFLICT (company_id, crm_id) DO UPDATE SET {} = {} RETURNING crm_id, id ''').format( sql.Identifier(db_table), # 动态表名 sql.SQL(',').join(map(sql.Identifier, keys)), # 插入字段列表 sql.SQL(',').join(map(sql.Identifier, update_keys)), # 更新字段列表 sql.SQL(',').join( map(lambda k: sql.SQL('EXCLUDED.{}').format(sql.Identifier(k)), update_keys) ) # 引用EXCLUDED的字段 ) # 直接执行,无需提前mogrify output = execute_values(cur, statement, args, fetch=True)
2. 验证批次处理行为(可选)
如果想临时验证批次拆分的影响,可以强制execute_values用单个批次执行所有数据:
output = execute_values(cur, statement, args, batch_size=len(args), fetch=True)
如果这样操作后问题消失,就完全确认是批次拆分时的语句构造冲突导致的。
3. 排查字段引用正确性
检查你的update_keys是否包含csm_id,如果包含,确保在DO UPDATE部分正确引用了EXCLUDED.csm_id——之前的excluded_keys如果是手动拼接的字符串,可能在批次拆分时出现解析错误,导致该字段没有被正确赋值,最终被设为NULL。
辅助调试建议
查看Postgres的详细错误日志,找到异常批次对应的SQL语句,对比正常批次和异常批次的语句差异,能更直接地定位语法或字段引用的问题。
内容的提问来源于stack exchange,提问作者gkv

