如何比较两个Pandas DataFrame并提取变更记录用于SQL更新
对比Pandas DataFrame并生成SQL更新用数据集
需求说明
需要编写Python脚本对比两个DataFrame:df1是从现有SQL表读取的旧数据,df2是API拉取的新数据。目标是筛选出任意字段值有变更或新增的记录,生成结构和原表一致的新DataFrame,用于更新SQL表。
之前用外连接的方法没得到正确结果,返回了完整的合并DataFrame,需要修正。
示例说明
- df1(旧数据):包含主键及业务字段,比如ID、Name、Age、City,部分记录如ID=1(Alice,25,NY)、ID=2(Bob,30,LA)
- df2(新数据):同结构,部分字段变更(如ID=1的Age变为26)、新增记录(ID=3)、字段变更(ID=4的City从CHI改为DAL)
- 期望输出:仅包含ID=1(Age变更)、ID=3(新增)、ID=4(City变更)的记录,用df2的新值,结构和原表一致
原代码问题
原代码仅通过df_merged.isna().any(axis=1)筛选存在空值的行,只能识别新增/删除的记录,无法检测到非空但值不同的字段变更,所以返回了全部数据。
修正后的解决方案
完整代码
import pandas as pd def compare_dataframes(df1, df2, pk_col): # 按主键外连接,保留两边所有记录 df_merged = pd.merge(df1, df2, on=pk_col, how='outer', suffixes=('_old', '_new')) # 获取所有非主键字段 non_pk_cols = [col for col in df1.columns if col != pk_col] # 标记存在字段变更的行 has_change = pd.Series(False, index=df_merged.index) for col in non_pk_cols: col_old = f"{col}_old" col_new = f"{col}_new" # 对比新旧值,处理空值相等的情况 change_mask = ~df_merged[col_old].fillna('').eq(df_merged[col_new].fillna('')) has_change = has_change | change_mask # 筛选出有变更的行,或仅在df2中存在的新增记录 only_in_df2 = df_merged[[f"{col}_old" for col in non_pk_cols]].isna().all(axis=1) df_diff = df_merged[has_change | only_in_df2] # 保留主键和新值字段,重命名回原字段名 result_cols = [pk_col] + [f"{col}_new" for col in non_pk_cols] df_diff = df_diff[result_cols].rename(columns={f"{col}_new": col for col in non_pk_cols}) return df_diff
代码关键点
- 用
fillna('')统一处理空值,避免两边都是空值时被误判为变更 - 遍历所有非主键字段,只要有一个字段值不同就标记为需要更新
- 单独识别新增记录:df1中无匹配主键的行(旧字段全为空)
- 最终结果只保留df2的新值,结构和原DataFrame完全一致,可直接用于SQL更新操作
测试验证
用示例数据测试:
df1:
| ID | Name | Age | City |
|---|---|---|---|
| 1 | Alice | 25 | NY |
| 2 | Bob | 30 | LA |
| 4 | Dave | 35 | CHI |
df2:
| ID | Name | Age | City |
|---|---|---|---|
| 1 | Alice | 26 | NY |
| 3 | Eve | 28 | MIA |
| 4 | Dave | 35 | DAL |
调用函数后返回的结果:
| ID | Name | Age | City |
|---|---|---|---|
| 1 | Alice | 26 | NY |
| 3 | Eve | 28 | MIA |
| 4 | Dave | 35 | DAL |
完全符合期望输出。
内容的提问来源于stack exchange,提问作者Mike Mann
相关产品推荐
相关产品推荐

