PostgreSQL Upsert非空约束异常排查(psycopg2+Supabase)
问题根源与解决方案
核心原因
test_col_2存在NOT NULL约束:你提到无法将该列设为NULL,这直接证明了该列被配置为不允许空值。- Upsert的INSERT分支触发约束:当你执行场景3的操作时,如果
test_row_7这一行不存在于数据库中,Upsert会执行INSERT逻辑,但你没有提供test_col_2的值,导致该列被插入NULL,触发NOT NULL约束。 - 若确认
test_row_7已存在,需检查SQL生成逻辑:你的代码理论上只会更新传入的列(test_col_1、test_col_3、test_col_4),不会修改test_col_2,这种情况下报错大概率是SQL语句生成错误,比如误将test_col_2加入更新列表。
验证步骤
先打印出生成的SQL语句排查问题,在cur.execute前添加代码:
print(f_s_sql_input)
场景3正确生成的SQL应该是:
INSERT INTO test_db (test_col_1, test_col_3, test_col_4) VALUES (%s, %s, %s) ON CONFLICT (test_col_1) DO UPDATE SET test_col_1 = excluded.test_col_1, test_col_3 = excluded.test_col_3, test_col_4 = excluded.test_col_4;
如果SQL与上述一致,说明问题就是目标行不存在,导致INSERT触发约束。
解决方案
方案1:仅更新已有行(不插入新行)
如果业务逻辑不需要插入新行,直接改用UPDATE语句:
# 调整SQL生成逻辑 f_s_col_names_update = [x+ " = %s" for x in f_l_col_names[1:]] f_s_col_names_sql_update = ", ".join(f_s_col_names_update) f_s_sql_input = f"UPDATE {f_s_sql_table_name} SET {f_s_col_names_sql_update} WHERE {f_l_col_names[0]} = %s;" # 执行更新:传入要更新的列值 + 主键值 update_values = list(f_l_t_row_values[0][1:]) + [f_l_t_row_values[0][0]] cur.execute(f_s_sql_input, update_values)
方案2:保留Upsert逻辑,确保INSERT分支满足约束
如果需要支持插入新行,必须在传入参数时包含test_col_2的值,或者修改表结构去掉test_col_2的NOT NULL约束(需符合业务规则)。
关于excluded关键字的说明
excluded是PostgreSQL Upsert中的虚拟表,存储你试图插入的行数据。当你未传入test_col_2时,excluded.test_col_2为NULL,但你的UPDATE逻辑并未修改test_col_2,所以只有INSERT分支会触发约束。
内容的提问来源于stack exchange,提问作者John Doe Python
相关产品推荐
相关产品推荐

