Google Ads数据同步至SQLite数据库时,keyword_text为空值/None导致重复插入的问题排查
嘿,我看了你的代码,发现问题大概率出在日期类型不匹配上,导致现有记录和新数据的主键元组无法正确匹配,从而触发了重复插入。咱们一步步来拆解和解决:
问题根源分析
你在google_ads_retrieving函数里把date字段转成了datetime.date对象,但SQLite本身没有原生的日期类型,会把这个对象自动存成字符串格式(比如'2024-05-20')。
当save_to_database读取现有数据时,从数据库拿到的date是字符串;但新数据的date是datetime.date对象,两者类型不同——哪怕值看起来一样,组成的主键元组(比如(datetime.date(2024,5,20), 'campaignA', '')和('2024-05-20', 'campaignA', ''))也会被判定为不相等。代码会误以为这是一条新记录,执行插入操作,最终导致重复。
至于keyword_text的None处理,你已经通过两次fillna('')转成了空字符串,本身逻辑是对的,只是被日期类型的问题掩盖了。
具体解决方案
1. 统一日期格式为字符串
在数据预处理阶段,把date转成和数据库存储一致的字符串格式,替代原来的datetime.date对象。修改google_ads_retrieving里的日期处理代码:
# 替换原来的dt.date,生成标准的'YYYY-MM-DD'格式字符串 df["date"] = pd.to_datetime(df["date"]).dt.strftime('%Y-%m-%d') df_conv["date"] = pd.to_datetime(df_conv["date"]).dt.strftime('%Y-%m-%d')
2. 清理数据库历史NULL数据(可选)
如果之前同步过未处理的NULL值到keyword_text字段,先执行一次数据库清理,确保现有数据的keyword_text都是空字符串:
UPDATE google_ads_data SET keyword_text = '' WHERE keyword_text IS NULL;
3. 优化主键匹配的类型一致性(可选)
为了避免其他类型不匹配的问题,可以在save_to_database里强制把所有主键列转成字符串,确保对比时完全一致:
# 读取现有数据构建主键时,统一转成字符串 existing_data = set() for row in cursor.fetchall(): existing_key = tuple(str(k) if k is not None else '' for k in row) existing_data.add(existing_key) # 处理新数据构建主键时,也统一转成字符串 for _, row in df.iterrows(): key = tuple(str(row[col]) if pd.notnull(row[col]) else '' for col in key_columns) if key in existing_data: # 执行UPDATE逻辑 update_query = f"UPDATE {table_name} SET " update_query += ", ".join([f"{col} = ?" for col in df.columns if col not in key_columns]) update_query += f" WHERE {' AND '.join([f'{col} = ?' for col in key_columns])}" update_values = [row[col] for col in df.columns if col not in key_columns] + list(key) cursor.execute(update_query, update_values) else: # 执行INSERT逻辑 insert_query = f"INSERT INTO {table_name} ({', '.join(df.columns)}) VALUES ({', '.join(['?' for _ in df.columns])})" cursor.execute(insert_query, row.tolist()) existing_data.add(key)
测试验证
修改完成后,重新运行数据同步逻辑,应该就不会再出现keyword_text为空时的重复插入问题了。
备注:内容来源于stack exchange,提问作者Davide

