Python/Django 过滤组中具有最大值的行

标签 python django group-by orm inner-join

我看到了对此的多个答案,但建议的解决方案都没有帮助我。

模型描述了各个单元的生产计划。生产计划每小时更新一次。每个生产计划被称为“层”,因为它们在白天相互“堆叠”。当然,下一层比上一层要短一个小时。

模型如下:

class PlanData(models.Model):
    plan_type = models.ForeignKey(PlanType, on_delete = models.CASCADE) # we only need type 2 here
    plan_ident = models.ForeignKey(ObjectConfig, on_delete = models.CASCADE) # decribes production unit
    plan_for_day = models.DateField() # the day of production cycle
    layer = models.IntegerField(null = True) 
      #'layer' production plan from specified hour to then of the day. 
      # layer 1 contains 24 values, layer 10 - 14 values
    hour = models.IntegerField() # hour of production
    val = models.FloatField(blank = True, null = True) # how much the unit should produce at that hour

我需要的是通过按 plan_ident 和小时分组来获取层数最大的 PlanData 来过滤 PlanData。

我想做的事情可以用 SQL 来完成

select a.plan_ident, a.hour, a.layer, a.val
from dbo.asbr_plandata a
inner join (
    select max(layer) 'mlayer',plan_ident_id, hour
    from dbo.asbr_plandata
    where datediff(day,plan_for_day,getdate()) = 0
    and plan_type_id = 2 and plan_ident_id in (24)
    group by plan_ident_id, hour) b 
        on a.hour  = b.hour 
       and a.layer = b.mlayer 
       and a.hour  = b.hour 
       and a.plan_ident_id = b.plan_ident_id
where datediff(day,a.plan_for_day,getdate()) = 0
and a.plan_type_id = 2 and a.plan_ident_id in (24)

是的,我可以使用以下方法获得每个组的最大层数:

pbr = PlanData.objects.filter(plan_for_day = timezone.now().date(), plan_type = 2, plan_ident__in = [10,12,13]).values('hour','plan_ident').annotate( Max('layer'))

但是我需要完整的数据,如果我在最后的某个地方添加值,我会得到所有的数据,而不仅仅是分组的值。

当然,我可以获取包含所有图层的字典列表,然后对其进行过滤,但我的知识有限,我什至不知道如何查找它。

如何通过仅选择具有最大值的行来过滤 QuerySet? 或者如何内部连接两个查询集? 或者如何通过对字典进行分组并获取最大值来过滤字典列表?

任何解决方案都有效。

最佳答案

假设我正确理解了您的问题,根据您的 SQL 方言,一种方法可能是使用 Window 函数,然后过滤结果。例如:

from django.db.models import Window, Max, F

result = PlanData.objects.filter(
    **your_filters
).annotate(
    max_layer=Window(
        expression=Max('layer'),
        partition_by=[F('hour'), F('plan_ident')],
    )
)

参见Django docs有关窗口函数的更多信息。

编辑:是的,忘了你不能在 WHERE 子句中使用窗口函数。但是您将能够在 Python 中更轻松地过滤结果,例如:

filtered = filter(lambda row: row.max_layer == row.layer, result)

或者,如果您想将结果保留为 QuerySet 形式,您可以使用 Subquery,例如:

from django.db.models import F, OuterRef, Subquery, IntegerField

sub_query = PlanData.objects.filter(
    **your_filters,
    hour=OuterRef('hour'),
    plan_ident=OuterRef('plan_ident'),
)

result = PlanData.objects.filter(
    **your_filters
).annotate(
    max_layer=Subquery(
        subquery.order_by('-layer').values('layer')[:1],
        output_field=IntegerField(),
    )
).filter(
    layer=F('max_layer')
)

关于Python/Django 过滤组中具有最大值的行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59151784/

相关文章:

python 数据结构 : map<string, vector<int>>

MySQL 对行组进行排序

python - 在 Django 中将请求响应转换为 DRF 响应的最优雅方法是什么?

python - 修补作为另一个类的属性的类实例属性

python - 根据字段值更改 Django 模型中的选择

python - 创建没有多个查询的 Django 表单集

python - Django:TypeError: 'x' 是此函数的无效关键字参数

python - 根据列的计数值的数据框子集

sql - 聚合具有优先级的 SQL 行

python - 除了Python中的中断