Django上传大型数据集时高效检查数据是否存在的方法
高效导入Excel数据到Django Student模型的优化方案
问题背景
需要将包含17000行数据的Excel表格导入Django的Student模型,但当前实现每次循环都会发起数据库查询,导致导入效率极低,现需高效检查数据是否已存在的优化方案。
现有代码
models.py
# no fields value are unique here, class Student(models.Model): name=models.CharField(max_length=100) address=models.CharField(max_length=100) age=models.CharField(max_length=100) rollno=models.CharField(max_length=100)
views.py
def upload_script(request): wb = openpyxl.load_workbook(excel_file) worksheet = wb["All DMAs"] for row in worksheet.iter_rows(): datacollection=row[0:9] name=datacollection[0] address=datacollection[1] rollno=datacollection[2] age=datacollection[3] # everytime this call the database which load more time if(Student.objects.filter(name=name,address=address,rollno=rollno,age=age).first()): continue student_obj=Student( name=name, address=address, rollno=rollno, age=age ) student_list.append(student_obj) if len(student_list)>1000: Student.objects.bulk_create(student_list) student_list=[] Student.objects.bulk_create(student_list)
优化方案
1. 添加联合唯一约束(可选但推荐)
在Student模型中添加联合唯一约束,既保证数据库层面的数据唯一性,又能加速后续批量查询:
class Student(models.Model): name=models.CharField(max_length=100) address=models.CharField(max_length=100) age=models.CharField(max_length=100) rollno=models.CharField(max_length=100) class Meta: # 四个字段组合唯一,同时生成联合索引加速查询 unique_together = ('name', 'address', 'rollno', 'age')
执行迁移更新数据库:
python manage.py makemigrations python manage.py migrate
2. 批量预查询现有数据,内存中比对
一次性查询所有已存在的学生标识(四个字段的组合)存入内存集合,循环导入时直接在内存中判断,避免频繁数据库查询:
优化后的views.py
def upload_script(request): wb = openpyxl.load_workbook(excel_file) worksheet = wb["All DMAs"] # 一次性获取所有已存在的学生组合,存入集合(O(1)时间复杂度查找) existing_student_keys = set( Student.objects.values_list('name', 'address', 'rollno', 'age') ) student_list = [] # 直接读取单元格值,跳过处理Cell对象的开销 for row in worksheet.iter_rows(min_row=2, values_only=True): # min_row=2跳过表头,按需调整 name = row[0] address = row[1] rollno = row[2] age = row[3] # 内存快速判断,无需触发数据库查询 if (name, address, rollno, age) in existing_student_keys: continue student_obj = Student( name=name, address=address, rollno=rollno, age=age ) student_list.append(student_obj) # 每1000条批量插入,减少数据库交互次数 if len(student_list) >= 1000: Student.objects.bulk_create(student_list) student_list = [] # 处理剩余未提交的数据 if student_list: Student.objects.bulk_create(student_list)
优化说明
- 批量预查询:将原有的O(n)次数据库查询缩减为1次,集合查找的时间复杂度为O(1),大幅降低数据库交互开销。
- Excel读取优化:
values_only=True直接返回单元格数值,避免处理Cell对象带来的错误和额外操作。 - 联合索引加速:联合唯一约束自动生成的索引,让预查询的
values_list执行效率更高。
内容的提问来源于stack exchange,提问作者BHUWAN PANDEY
相关产品推荐
相关产品推荐

