优化Django子查询(目前使用`annotate`和`Count`)

时间:2017-05-04 08:31:12

标签: python django postgresql

使用: Django 1.11,Postgres 9.6

我需要优化Django ORM查询以供Django Rest Framework使用。查询必须返回以下记录:

  1. target字段
  2. 中匹配ID列表
  3. 将结果集限制为source ID在集合中出现多次的记录。
  4. 当前的方法是使用annotateCount创建子查询,但每个请求后面的处理量增加到分页意味着应用程序导致超时或行为非常慢。

    如果Postgres可以在服务器上做任何事情作为原始查询,我就可以了。

    型号:

    class Relationship(models.Model):
        id = models.AutoField(primary_key=True)
        source = models.BigIntegerField(db_index=True)
        target = models.BigIntegerField(db_index=True)
    

    查看摘要

    match_list = [123, 456, 789] # dummy data for example
    queryset = Relationship.objects.filter(target__in=match_list)
    sub_queryset = (Relationship.objects.filter(target__in=_match_list)
                                        .values('source')
                                        .annotate(source_count=Count("source"))
                                        .filter(source_count__gt=1)
                                        )
    sub_ids = [i["source"] for i in sub_queryset]
    queryset = (queryset.filter(source__in=sub_ids)
                )
    

    API将目标ID列表作为参数,并使用连接到该目标的所有source ID列表进行响应。但是,我正在过滤查询集,只返回连接到两个或多个source的{​​{1}}条记录。

    作为背景,结果查询集将由Django Rest Framework提供,并且它当前正在导致超时,因为请求的指数越长越多

    注意 :我把它放在SO上因为它导致我的请求超时并因此导致错误。我知道我可以延长超时持续时间,但宁愿优化查询。我考虑过CodeReview,但认为这更合适。

    编辑1 :根据target的建议,它当前是一个单独的子查询,因为@albar / annotate操作仅在返回值时有效,没有完整的记录

1 个答案:

答案 0 :(得分:1)

一种可能的解决方案是使用相关子查询:

duplicates = Relationship.objects.exclude(
    id=OuterRef('id')
).filter(source=OuterRef('source'))

Relationship.objects.annotate(
    duplicated=Exists(duplicates)
).filter(
    duplicated=True
)

这是建立一个查询集(无法单独评估),该查询集将仅包含与主查询集的“源”值重复的元素。然后过滤元素,以便仅选择那些元素。

在django中没有.annotate(...).filter(...)的情况下目前无法做到这一点,但是有时会带来性能影响:仅在数据库中(在WHERE子句中)对其进行评估就可以对性能做出重大改进。