如何使用查找和条件相关插入在django中执行插入?

时间:2017-04-17 13:19:48

标签: python sql django database sqlite

我正在尝试通过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后端(只是为了方便而不是偏好或功能)

非常感谢,

安迪

0 个答案:

没有答案