【问题标题】:Django TruncDay limit by largest groupingDjango TruncDay 最大分组限制
【发布时间】:2020-02-20 15:12:49
【问题描述】:

我正在尝试绘制一个日系列图,它将显示一天系列中部门的员工人数。这里的问题是我想找到一种方法来限制员工最多的部门,这样我就不需要画太多线并给数据库带来压力。 (例如限制在员工出勤率最高的前 3 个部门)

我正在使用: Django 1.11.x Postgres 9.4

我们的目标是创建这种类型的日系列图表。有部门分组和员工人数。

我已经设法通过以下代码实现它:

from datetime import date, datetime
from django.db.models import Count
from django.db.models.functions import (
    TruncDate, TruncDay, TruncHour, TruncMinute, TruncSecond,
    )

emp_by_day = Attendance.objects.annotate(day=TruncDay('created_at')).values('day', 'division_id').annotate(cnt=Count('employee_id', distinct = True)).order_by('day')

for exp in emp_by_day:
    print(exp['day'], exp['division_id'], exp['cnt'])

然而,它目前输出这样的显示(我通常很高兴但想限制它):

              employee count<->
              division_id<->
<---day----------------->
2019-10-22 00:00:00+00:00 15 6
2019-10-22 00:00:00+00:00 16 6
2019-10-22 00:00:00+00:00 18 5
2019-10-22 00:00:00+00:00 20 4
2019-10-22 00:00:00+00:00 21 12 <-- largest 3
2019-10-22 00:00:00+00:00 25 14 <-- largest 3
2019-10-22 00:00:00+00:00 28 12 <-- largest 3
2019-10-23 00:00:00+00:00 15 6
2019-10-23 00:00:00+00:00 16 5
2019-10-23 00:00:00+00:00 18 2
2019-10-23 00:00:00+00:00 20 3
2019-10-23 00:00:00+00:00 21 14 <-- largest 3
2019-10-23 00:00:00+00:00 25 17 <-- largest 3
2019-10-23 00:00:00+00:00 28 13 <-- largest 3
2019-10-24 00:00:00+00:00 15 2
2019-10-24 00:00:00+00:00 16 6
2019-10-24 00:00:00+00:00 18 5
2019-10-24 00:00:00+00:00 20 4
2019-10-24 00:00:00+00:00 21 13 <-- largest 3
2019-10-24 00:00:00+00:00 25 12 <-- largest 3
2019-10-24 00:00:00+00:00 28 10 <-- largest 3

我的目标是产生这个(限制在最大的 3 个部门):

2019-10-22 00:00:00+00:00 21 12 <-- largest 3
2019-10-22 00:00:00+00:00 25 14 <-- largest 3
2019-10-22 00:00:00+00:00 28 12 <-- largest 3
2019-10-23 00:00:00+00:00 21 14 <-- largest 3
2019-10-23 00:00:00+00:00 25 17 <-- largest 3
2019-10-23 00:00:00+00:00 28 13 <-- largest 3
2019-10-24 00:00:00+00:00 21 13 <-- largest 3
2019-10-24 00:00:00+00:00 25 12 <-- largest 3
2019-10-24 00:00:00+00:00 28 10 <-- largest 3

请告诉我如何才能产生这样的预期输出(将其限制为最大的 3 个部门)

【问题讨论】:

  • 我可以用 SQL 提供解决方案吗?我在 ORM 方面不是很好。但是,我也会尝试在其中发布解决方案。
  • 您想将其限制在当天员工出勤率最高的前 3 个部门,还是在报告时间或...的平均时间?
  • 是当天员工出勤率最高的前 3 个部门,并将这 3 个部门用于时间序列。无论如何,如果还有其他人认为它很好,只要我可以在前 3 名的查询中限制它

标签: django postgresql time-series django-queryset limit


【解决方案1】:

你应该使用Rank()窗口函数来过滤掉结果。

逻辑:

假设您希望按照 day 进行分组

您应该根据每天划分的cnt 值对每一行进行排名。最高的将获得第一名,依此类推。现在您应该过滤掉排名在 1 到 3 之间的结果。

继续查询

emp_by_day.annotate(rank=Window(
expression=Rank(),
order_by=F('cnt').desc(),
partition_by=[F('day')])).filter(rank__range=(1,3))

注意:如果cnt的值多于一行,则两行或多行的排名相同。因此,您可以获得超过 3 行。如果您只想要前 3 行,请使用 RowNumber() 而不是 Rank()。

Postgres 示例查询:

select * from (
 select *, rank() over (partition by day order by cnt desc) as rank from 
 (
  select emp_id,day,count(emp_id) as cnt from attendance group by emp_id,day  
  order by day
 ) as T
) as Temp where rank between 1 and 3;

将 rank() 替换为 row_number() 以仅获取前 3 行。

更新

Django 1.11 不支持window()。但是,您可以参考 following gist,它将此功能从 Django 2 反向移植到 1.11。

注意:我没有测试过。但是,OP 创建者对其进行了测试,并且可以正常工作。

【讨论】:

  • AttributeError: 'QuerySet' object has no attribute 'objects'
  • 哇,不错的解决方案。没有意识到这一点。
  • django 1.11 不支持 ---> django.db.models 导入窗口,可能是一个不错的解决方案。
  • @Axil 如果有帮助,请投票,如果它解决了您的任务,请接受答案。答案已经编辑以解决您在 cmets 中提出的第一个问题。
  • @Axil 如果您有任何其他疑问,请提及
【解决方案2】:

首先找出您要绘制图表的部门是什么(我们称之为集合best_divisions),然后在您的查询中过滤它们。

Attendance.objects.filter(division__in=best_divisions).annotate(day=…

例如,为了找到部门,您可以:

best_divitions = Division.objects.annotate(
    total_attendance=Count("attendance__employee", distinct=True),
).order_by("-total_attendance")[:3]

【讨论】:

  • 这看起来很接近,best_divisions,不知何故我认为需要全部金额。我们如何获得当前日/月/年的前 3 名?
  • 然后将其用作 best_divisions 进行过滤。我喜欢这个主意,但我想选择当前日/月/年的前 3 个部门(而不是整个数据库)——我们可以以日为例
  • 在您的解决方案中,您也没有指定任何时间范围。使用子查询仅计算您感兴趣的注释以确定最佳划分。 docs.djangoproject.com/en/2.2/ref/models/expressions/…
  • 因为我使用 TruncDay,它是时间序列/范围
猜你喜欢
  • 1970-01-01
  • 2016-08-15
  • 1970-01-01
  • 2023-03-10
  • 2016-11-01
  • 2016-11-22
  • 2019-02-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多