Ограничение Django TruncDay по наибольшей группировке - PullRequest
4 голосов
/ 24 октября 2019

Я пытаюсь нарисовать график дневных рядов, в котором будет показано количество сотрудников подразделений в дневном ряду. Проблема здесь в том, что я хочу найти способ ограничиться разделением с большинством сотрудников, поэтому мне не нужно рисовать слишком много линий и подчеркивать базу данных. (например, ограничьте его 3 верхними подразделениями с наибольшей посещаемостью сотрудников)

Я использую: Django 1.11.x Postgres 9.4

Цель состоит в том, чтобы создать графики дневных рядов такого рода. имеет группировку деления и численность работников.

graphing

Мне удалось добиться этого с помощью следующего кода:

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 деления)

Ответы [ 2 ]

1 голос
/ 01 ноября 2019

Вы должны использовать Rank() оконную функцию для фильтрации результатов.

Логика:

Предполагая, что вы хотите группироватьза day

Вы должны присвоить ранг каждой строке на основе значения счетчика cnt, разделенного на день. Самый высокий получит 1-й ранг и так далее. Теперь вы должны отфильтровать результат, который имеет рейтинг от 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(). Тем не менее, вы можете сослаться на после gist , который переносит эту функциональность с Django 2 на 1.11.

Примечание: Я не тестировал его. Однако создатель OP проверил его, и он работает.

1 голос
/ 01 ноября 2019

Сначала выясните, какие деления вы хотите отобразить (давайте назовем этот набор 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]
...