我对此有多个答案,但是没有建议的解决方案对我有帮助。
模型描述了各种单元的生产计划。生产计划每小时更新一次。每个生产计划在一天中彼此“堆叠”时称为“层”。自然,下一个“层”比上一个“层”短一小时。
模型如下:
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和hour分组来获取那些层数最大的层,从而过滤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? 或如何内部联接两个查询集? 或者如何通过将字典分组并获取最大值来过滤字典列表?
任何解决方案都可以使用。
答案 0 :(得分:0)
假设我已经正确理解了您的问题,根据您的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')],
)
)
有关Window
函数的更多信息,请参见Django docs。
EDIT :是的,您忘记了WHERE
子句中没有Window函数。但是您将能够在Python中更轻松地过滤结果,例如:
filtered = filter(lambda row: row.max_layer == row.layer, result)
或者,如果您想将结果保留为Subquery
格式,则可以使用QuerySet
,例如:
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')
)
答案 1 :(得分:0)
将代码略微修改为上面的正确答案。
result = PlanData.objects.filter(
**other_filters,
layer = Subquery(
PlanData.objects.filter(
plan_for_day = OuterRef('plan_for_day'),
plan_ident = OuterRef('plan_ident'),
hour = OuterRef('hour')
).values( 'plan_for_day',
'plan_ident',
'hour'
).annotate(
max_layer = Max('layer')
).values_list('max_layer')
)
)