使用大子查询优化Django查询

时间:2018-02-20 15:36:07

标签: python django django-orm

我有一个包含ProfileRelationship模型的数据库。我没有在模型中明确地链接它们(因为它们是第三方ID,并且它们可能在两个表中都不存在),但sourcetarget字段映射到一个或多个{{1通过Profile字段对象:

id

我的查询需要从from django.db import models class Profile(models.Model): id = models.BigIntegerField(primary_key=True) handle = models.CharField(max_length=100) class Relationship(models.Model): id = models.AutoField(primary_key=True) source = models.BigIntegerField(db_index=True) target = models.BigIntegerField(db_index=True) 列中获取100个值的列表,这些值尚不存在Relationship.source。然后,该列表将用于从第三方收集必要的数据。下面的查询有效,但随着表的增长(10m +),SubQuery变得非常大而且速度慢。

有关如何优化此建议的任何建议?后端是PostgreSQL,但如果可能的话我想使用原生的Django ORM。

编辑:有一个额外的复杂程度会导致查询速度变慢。并非所有ID都能保证返回成功,这意味着它们将继续“不存在”并使程序处于无限循环中。所以我添加了一个Profile.idfilter来输入前一批100中的最高order_by。这将导致一些问题,因此最初错过它会道歉。

id

提前感谢任何建议!

1 个答案:

答案 0 :(得分:0)

对于未来的搜索者,我在这里绕过了__in查询,并且能够加快结果。

from django.db.models import Subquery
from django.db.models import Count  # New

user = Profile.objects.get(handle="philsheard")
subq = Profile.objects.filter(profile_id=OuterRef("source"))  # New queryset to use within Subquery
rels = Relationship.objects.order_by(
    "source"
).annotate(
    # Annotate each relationship record with a Count of the times that the "source" ID
    # appears in the `Profile` table. We can then filter on those that have a count of 0
    # (ie don't appear and therefore haven't yet been connected)
    prof_count=Count(Subquery(subq.values("id")))
).filter(
    target=user.id,
    prof_count=0
).filter(
    source__gt=max_id_from_previous_batch  # An integer representing a previous `Relationship.source` id
).values_list(
    "source", flat=True
)

我认为这更快,因为查询将在达到所需的100项后完成(而不是每次与1m + ID的列表进行比较)。