最大分组的 Django TruncDay 限制
Django TruncDay limit by largest grouping
我正在尝试绘制日系列图,它将显示日系列中部门的员工人数。这里的问题是我想找到一种方法来限制员工最多的部门,这样我就不需要画太多线并给数据库施加压力。 (例如将其限制为员工出勤率最高的前 3 个部门)
我正在使用:
姜戈 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 格)
首先找出您要绘制的分区是什么(我们称其为集合 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]
您应该使用 Rank()
window 函数来过滤结果。
逻辑:
假设您想要按 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()
by row_number()
以仅获取前 3 行。
更新
Django 1.11 不支持 window()
。但是,您可以参考 following gist,它将此功能从 Django 2 反向移植到 1.11。
注:我没测试过。但是,OP 创建者对其进行了测试并且它有效。
我正在尝试绘制日系列图,它将显示日系列中部门的员工人数。这里的问题是我想找到一种方法来限制员工最多的部门,这样我就不需要画太多线并给数据库施加压力。 (例如将其限制为员工出勤率最高的前 3 个部门)
我正在使用: 姜戈 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 格)
首先找出您要绘制的分区是什么(我们称其为集合 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]
您应该使用 Rank()
window 函数来过滤结果。
逻辑:
假设您想要按 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()
by row_number()
以仅获取前 3 行。
更新
Django 1.11 不支持 window()
。但是,您可以参考 following gist,它将此功能从 Django 2 反向移植到 1.11。
注:我没测试过。但是,OP 创建者对其进行了测试并且它有效。