我正在尝试通过django优化大型数据集的数据库插入。 这是一个简单的示例模型,足以证明我面临的问题。
class School(models.Model):
name = models.CharField(max_length=32)
postcode = models.CharField(max_length=8)
class Student(models.Model):
name = models.CharField(max_length=32)
school = models.ForeignKey(School)
last_updated = models.DateTimeField(blank=True, null=True)
first_registered = models.DateTimeField(blank=True, null=True)
鉴于上面的示例模型。我现在可以按以下方式创建行
school = app.models.School.objects.get_or_create(name='School 1', postcode='AB12 3CD')
student = app.models.Student.objects.create(name='Student 1', school=school[0])
这适用于简单的"入门"但是,当扩展到更大的数据集和更复杂的关系模型时,存在潜在的问题。
我将定义更真实的世界问题。
从本质上讲,每天都有一个列表,其中包含所有学生和他们所在学校的当前快照。
鉴于上述情况,不难想象也许有10,000所学校拥有5,000,000名学生,如果在全球范围内寻找,可能会达到数十亿。
对数据的一些观察包括,每天约95%的数据是重复的。因此,可以通过简单的查询快速过滤此数据。 目前,我有以下代码,可以很好地过滤现有数据,还可以在单个SQL语句中有条件地更新某些字段。
# See if the student already exists and is assigned to the school in the data set.
# If it does exist, get the database to update the last updated date and the first registration dates if needs be.
#
# Note: These updates are necessary as it is possible that data is imported in non-chronological order.
updated_fields = app.models.Student.objects.filter(
name=data['student_name'],
school__name=data['school_name'],
school__postcode=data['school_postcode'],
).update(
last_updated=Case(
When(last_updated__lt=data['last_updated'], then=data['last_updated']),
default=F('last_updated')
),
first_registered=Case(
When(first_registered_gt=data['first_registered'], then=data['first_registered']),
default=F('first_registered')
)
)
我面临的问题是当我插入新数据时,我遇到了多次数据库查询。学校的数量远远少于学生,而且学费增长速度较慢。 除了初始导入之外,在所有后续的每日更新中,99.9%的时间学校数据已经存在于数据库中,学生只需要分配。
目前我的导入按照原始方式工作如下,并且有比显示更多的FK查找,因此我热衷于减少数据库往返。
for entry in data:
school = app.models.School.objects.get_or_create(name=entry['school_name'], postcode=entry['school_postcode'])
student = app.models.Student.objects.create(name=data['student_name'], school=school[0])
我想要做的是将get_or_create移动到create语句中,如果它在插入点不存在则让它在数据库中创建。我觉得这样做的正确方法是让数据库完成所有工作。
这个想法是在每次插入时传递所有学生和学校的信息。然后,插入查询应尝试查找学校信息并将该条目分配给FK for Student。 如果没有返回结果,则应创建学校,然后分配生成的FK。
我发现了类似的SQL问题。 How to insert records to SQL with looked up values? 我认为这就是我想要的,但有一个关键的区别,我的查询应该在查找失败的情况下创建条目
我想知道是否有人知道如何在django中执行此操作,可能使用WHERE,CASE等功能。? 如果通过ORM无法实现,那么欢迎使用RAW SQL提供一些帮助。
仅供参考我目前正在运行django 1.10并且升级不是问题。我正在使用sqlite后端(只是为了方便而不是偏好或功能)
非常感谢,
安迪