Python 优化Django子查询(当前使用'annotate'和'Count')
使用:Django 1.11,Postgres 9.6 我需要优化Django ORM查询以供Django Rest框架使用。查询必须返回以下记录:Python 优化Django子查询(当前使用'annotate'和'Count'),python,django,postgresql,Python,Django,Postgresql,使用:Django 1.11,Postgres 9.6 我需要优化Django ORM查询以供Django Rest框架使用。查询必须返回以下记录: 在其目标字段中匹配ID列表 将结果集限制为源ID在集合中出现多次的记录 当前的方法是使用annotate和Count创建子查询,但是添加到分页的每个请求背后的处理量意味着应用程序可能会导致超时或非常缓慢的行为 如果有任何事情可以通过服务器上的Postgres作为原始查询来完成的话,我可以接受 型号: class Relationship(model
目标
字段中匹配ID列表源ID在集合中出现多次的记录
annotate
和Count
创建子查询,但是添加到分页的每个请求背后的处理量意味着应用程序可能会导致超时或非常缓慢的行为
如果有任何事情可以通过服务器上的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)
)
查看片段:
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列表作为参数,并以连接到该目标的所有源ID列表作为响应。但是,我正在过滤查询集,以仅返回连接到两个或多个target
s的source
记录
作为背景,结果queryset将由Django Rest框架提供服务,它目前正在导致超时,因为请求越长,超时的时间就越长
注意:我之所以这样做是因为它会导致我的请求超时,从而导致错误。我知道我可以延长超时时间,但更愿意优化查询。我考虑过CodeReview,但觉得这更合适
编辑1:根据@albar
的建议,它目前是一个单独的子查询,因为注释
/计数
操作仅在返回值而非完整记录时有效一个可能的解决方案是使用相关子查询:
duplicates = Relationship.objects.exclude(
id=OuterRef('id')
).filter(source=OuterRef('source'))
Relationship.objects.annotate(
duplicated=Exists(duplicates)
).filter(
duplicated=True
)
这样做的目的是构建一个queryset(不能单独计算),其中只包含与主queryset的“源”值重复的元素。然后它过滤元素,以便只选择那些元素
在django中,如果没有.annotation(…).filter(…)
,目前不可能做到这一点,但是有时这会对性能产生影响:在数据库中(WHERE子句中)只能对一个进行评估,可以大大提高性能。可以简单地在目标和源上添加索引。感谢@e4c5,这一点很好,但已经提到了——我在这两个字段上都有索引(请参见模型-“db_index=True”),所以我认为这是注释/计数聚合步骤。我真的不理解您查询的逻辑,也不理解为什么要执行这么多步骤。我认为您需要的查询很简单:Relationship.objects.filter(target\u in=\u match\u list)、annotate(source\u count=count(“source”)).filter(source\u count\u gt=1)
@albar我最初尝试过这种方法,但注释只应用于单个记录,因此source\u count
值永远不会超过1。我必须执行.values
操作才能在整个结果集中启用聚合。我想答案可能在于做一个子查询而不是多个查询,但我在这方面还没有太多经验。@Phil Sheard好的,我现在明白了。不容易…这是一个好主意,马修-谢谢你发布它。我发布查询已经有一段时间了,但我仍然在使用代码库,所以我将根据我最终使用的解决方案对其进行测试。