Django导入CSV时忽略已有重复数据,允许同批次重复
高效导入CSV并忽略数据库已存在记录的方案
针对你的需求,这里提供两种比“查询首尾日期范围再比对”更高效的实现方式,核心都是减少数据库查询次数,同时满足“允许同一次导入内重复、忽略库中已有记录”的要求:
方案一:利用ORM批量查询+过滤,适合Django原生写法
这种方式仅需1次数据库查询,就能过滤掉所有已存在的记录,再批量插入剩余数据:
- 读取CSV数据:完整读取CSV内容到内存,保留所有重复项:
import csv from datetime import datetime from decimal import Decimal from myapp.models import Transaction transactions_data = [] with open('transactions.csv', 'r') as f: reader = csv.DictReader(f) for row in reader: # 根据CSV格式转换字段类型,示例格式可自行调整 transaction = { 'date': datetime.strptime(row['date'], '%Y-%m-%d').date(), 'description': row['description'].strip(), 'amount': Decimal(row['amount']) } transactions_data.append(transaction)
- 提取唯一键集合:避免重复查询,先把所有记录的
(date, description, amount)组合提取为唯一集合:
unique_keys = {(t['date'], t['description'], t['amount']) for t in transactions_data}
- 批量查询已存在记录:用一次查询找出数据库中已有的组合:
from django.db.models import Q # 构造多条件查询 query = Q() for date, desc, amt in unique_keys: query |= Q(date=date) & Q(description=desc) & Q(amount=amt) # 仅查询需要的字段,转成集合方便快速比对 existing_records = set(Transaction.objects.filter(query).values_list('date', 'description', 'amount'))
- 过滤并批量插入:过滤掉已存在的记录,用
bulk_create批量插入剩余数据:
to_insert = [ Transaction(**t) for t in transactions_data if (t['date'], t['description'], t['amount']) not in existing_records ] if to_insert: Transaction.objects.bulk_create(to_insert)
方案二:利用SQLite原生语句+临时表,适合大数据量场景
如果CSV数据量极大,ORM批量查询存在性能瓶颈,可以直接在数据库层面完成操作,天然支持保留同批次重复记录:
- 创建临时表:
from django.db import connection with connection.cursor() as cursor: # 创建与Transaction结构一致的临时表 cursor.execute(""" CREATE TEMPORARY TABLE temp_transaction ( date DATE, description TEXT, amount DECIMAL ) """)
- 批量导入CSV到临时表:
with connection.cursor() as cursor: params = [(t['date'], t['description'], t['amount']) for t in transactions_data] cursor.executemany(""" INSERT INTO temp_transaction (date, description, amount) VALUES (?, ?, ?) """, params)
- 从临时表插入正式表,忽略已存在记录:
with connection.cursor() as cursor: # 通过LEFT JOIN筛选出正式表中不存在的记录 cursor.execute(""" INSERT INTO transaction (date, description, amount) SELECT t.date, t.description, t.amount FROM temp_transaction t LEFT JOIN transaction tr ON t.date = tr.date AND t.description = tr.description AND t.amount = tr.amount WHERE tr.id IS NULL """) connection.commit() # 临时表会在数据库连接关闭后自动销毁,无需手动删除
这个方案的优势是所有操作在数据库内部完成,避免了大量数据在Python与数据库间传输,性能更高。
内容的提问来源于stack exchange,提问作者speg
相关产品推荐
相关产品推荐

