多个注释求和项得到膨胀答案(multiple annotate Sum terms yields i

2019-06-27 07:54发布

在下面的设置,我想有一个项目清单,各有其所有任务持续时间的总和(如tasks_duration)注释一个QuerySet及其所有任务的子任务持续时间的总和(如subtasks_duration)。 我的模型(简体)是这样的:

class Project(models.Model):
    pass

class Task(models.Model):
    project = models.ForeignKey(Project)
    duration = models.IntegerField(blank=True, null=True)

class SubTask(models.Model):
    task = models.ForeignKey(Task)
    duration = models.IntegerField(blank=True, null=True)

我让我的QuerySet是这样的:

Projects.objects.annotate(tasks_duration=Sum('task__duration'), subtasks_duration=Sum('task__subtask__duration'))

相关的行为解释Django的注释()多次导致错误的答案 ,我收到了tasks_duration是远高于它应该是。 所述多个注释(萨姆())的条款得到在所得SQL多个左内联接。 只有一个单一的注释(总和()),期限为tasks_duration,结果是正确的。 不过,我想有两个tasks_duration和subtasks_duration。

什么是做这个查询一个合适的方式? 我有一个工作的解决方案,它是每个项目,但是这果然unusably缓慢。 我也有类似的一个额外的()调用工作的东西,但我真的很想知道,如果我要的是可以用纯Django的。

Answer 1:

该错误报道这里 ,但它甚至没有在Django 1.11尚未解决。 该问题涉及到反向关系连接两个表。 请注意,不同的参数可以很好地用于计数,但不和。 所以,你可以使用一个技巧,写一个ORM象下面这样:

 Projects.objects.annotate(
      temp_tasks_duration=Sum('task__duration'),
      temp_subtasks_duration=Sum('task__subtask__duration'),
      tasks_count=Count('task'),
      tasks_count_distinct=Count('task', distinct=True),
      task_subtasks_count=Count('task__subtask'),
      task_subtasks_count_distinct=Count('task__subtask', distinct=True),
 ).annotate(
      tasks_duration=F('temp_tasks_duration')*F('tasks_count_distinct')/F('tasks_count'),
      subtasks_duration=F('temp_subtasks_duration')*F('subtasks_count_distinct')/F('subtasks_count'),
 )

更新:我发现,你需要使用子查询。 在下面的解决方案,首先您筛选相关任务的outerref(OuterRef对外部查询的引用,因此任务被过滤为每个项目),那么你组的任务由“项目”,使得总量适用于所有的每个项目和回报,如果任何任务存在的项目只是一个结果的任务(您已通过“项目”过滤,然后由同一领域进行分组;这就是为什么只有一个组可以有。)或无其他。 其结果将是无如果项目没有任务,这意味着我们不能用[0]选择计算的总和。

from django.db.models import Subquery, OuterRef
Projects.objects.annotate(
    tasks_duration=Subquery(
        Task.objects.filter(
            project=OuterRef('pk')
        ).values(
            'project'
        ).annotate(
            the_sum=Sum('task__duration'),
        ).values('the_sum')[:1]
    ),
    subtasks_duration=Sum('task__subtask__duration')
)

运行该代码将只发送一个查询到数据库中,这样的表现是伟大的。



Answer 2:

我得到这个错误也是如此。 完全相同的代码。 它的工作原理,如果我单独做了汇总,但一旦我试图在同一时间得到两个和,其中一人获得2倍以上,其他3倍。

我不知道为什么Django的行为这种方式。 我在这里提交bug报告: https://code.djangoproject.com/ticket/19011你可能有兴趣关注它。



文章来源: multiple annotate Sum terms yields inflated answer