代码之家  ›  专栏  ›  技术社区  ›  Phil Sheard

使用大型子查询优化Django查询

  •  2
  • Phil Sheard  · 技术社区  · 6 年前

    我有一个数据库包含 Profile Relationship 模型。我没有在模型中显式链接它们(因为它们是第三方ID,可能还不存在于两个表中),但是 source target 字段映射到一个或多个 轮廓 对象通过 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)
    

    我的查询需要从 Relationship.source 尚未作为 Profile.id . 该列表将用于从第三方收集必要的数据。下面的查询可以工作,但随着表的增长(10m以上),子查询变得非常大且速度很慢。

    关于如何优化这一点,有什么建议吗?后端是PostgreSQL,但如果可能的话,我想使用原生Django ORM。

    编辑:有一个额外的复杂性水平,这将有助于缓慢的查询。并非所有ID都能保证返回成功,这意味着它们继续“不存在”,并使程序处于无限循环中。所以我添加了一个 filter order_by 输入最高 身份证件 从上一批100个。这将导致一些问题,因此,很抱歉最初错过了它。

    from django.db.models import Subquery
    user = Profile.objects.get(handle="philsheard")
    qs_existing_profiles = Profiles.objects.all()
    rels = TwitterRelationship.objects.filter(
        target=user.id,
    ).exclude(
        source__in=Subquery(qs_existing_profiles.values("id"))
    ).values_list(
        "source", flat=True
    ).order_by(
        "source"
    ).filter(
        source__gt=max_id_from_previous_batch  # An integer representing a previous `Relationship.source` id
    )
    

    提前感谢您的建议!

    1 回复  |  直到 6 年前
        1
  •  0
  •   Phil Sheard    6 年前

    对于未来的搜索者,以下是我如何绕过 __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个项目,查询就会完成(而不是每次与1百万个以上ID的列表进行比较)。