使用awswrangler的redshift.to_sql实现Upsert时仅更新指定列的问题
解决Redshift Upsert时仅更新指定列、保留其他列值的问题
我明白你的困扰——用wr.redshift.to_sql的upsert模式时,没指定的列被意外设为NULL,这确实不是预期的行为。咱们来拆解问题原因,然后给出直接的解决方案:
问题根源
当你使用mode="upsert"时,wr.redshift.to_sql会生成INSERT ... ON CONFLICT的SQL语句。默认情况下,对于DataFrame中没有包含的列,工具会在冲突更新时将它们设为NULL(因为SQL里没有给这些列赋值)。而use_column_names=True只是让工具用DataFrame的列名去匹配表的列,并没有限制只更新指定列的作用。
正确解决方案:用update_columns指定更新范围
wr.redshift.to_sql其实提供了专门的参数来解决这个问题——update_columns,配合主键/冲突条件,就能精准控制只更新你需要的列,其他列保持原有值。
步骤1:确定匹配目标行的唯一标识
首先你需要明确,用什么条件找到要更新的那一行(比如表的主键,或者某个唯一约束列)。假设你的test_db表有主键id,且你知道要更新的行的id值是target_id。
步骤2:修改代码,添加关键参数
import pandas as pd import awswrangler as wr # 构造DataFrame:必须包含匹配目标行的唯一键(比如id),加上要更新的col_id和slug target_id = 1 # 替换成你要更新的行的实际id值 df = pd.DataFrame( [[target_id, datas.get("collection_id"), datas.get("slug")]], columns=["id", "col_id", "slug"] ) # 执行upsert,指定主键和要更新的列 wr.redshift.to_sql( df=df, table="test_db", schema="offchain", con=con, use_column_names=True, mode="upsert", primary_keys=["id"], # 用主键判断冲突,找到要更新的行 update_columns=["col_id", "slug"] # 仅更新这两个列,其他列保持原值 )
自定义冲突条件(如果不用主键)
如果你的表没有主键,而是用其他唯一约束来匹配行,可以用upsert_condition参数替代primary_keys:
wr.redshift.to_sql( df=df, table="test_db", schema="offchain", con=con, use_column_names=True, mode="upsert", upsert_condition="your_unique_column = EXCLUDED.your_unique_column", # 自定义冲突匹配条件 update_columns=["col_id", "slug"] )
为什么这个方法有效
primary_keys/upsert_condition告诉工具:当DataFrame中的某行和表中现有行在指定列上冲突时,执行更新操作。update_columns明确指定:冲突发生时,只更新这两个列的值,表中其他列(比如tag)会保留原有数据,不会被设为NULL。
不推荐的方法(仅供参考)
如果你不想用update_columns,也可以先查询目标行的其他列值,把它们加入DataFrame后再执行upsert。但这种方法需要多一次查询,效率低且存在并发更新风险,所以不推荐:
# 查询目标行的tag列值 existing_tag = wr.redshift.read_sql( "SELECT tag FROM offchain.test_db WHERE id = %s", con=con, params=[target_id] ).iloc[0]["tag"] # 把tag列加入DataFrame df["tag"] = existing_tag # 再执行upsert(此时所有列都有值,不会被设为NULL) wr.redshift.to_sql(df=df, table="test_db", schema="offchain", con=con, use_column_names=True, mode="upsert", primary_keys=["id"])
内容的提问来源于stack exchange,提问作者Beşir Kassab
相关产品推荐
相关产品推荐

