Какой Django ORM или сырой SQL я должен написать, чтобы получить именно то, что мне нужно

Я использую Postgresql, так как у меня есть модель

class EventsList(CreatedUpdatedMixin):
    start = models.DateTimeField()
    end = models.DateTimeField()
    is_inner = models.BooleanField()

Предположим, что у меня есть такие записи в БД: | start | end | is_inner | | --- | --- | --- | | 2021-12-09 14:30:12 | 2021-12-09 15:00:21 | true | | 2021-12-09 14:00:05 | 2021-12-10 21:00:15 | false | | 2021-12-10 09:00:39 | 2021-12-10 09:30:50 | true | | 2021-12-10 14:00:00 | 2021-12-11 15:00:00 | true | | 2021-12-14 10:00:00 | 2021-12-14 11:00:00 | true | | 2021-12-13 13:30:00 | 2021-12-16 14:30:00 | false | | 2021-12-14 13:10:00 | 2021-12-15 00:30:00 | true | | 2021-12-14 10:30:00 | 2021-12-16 13:34:00 | false | | 2021-12-15 13:30:00 | 2021-12-15 18:30:00 | true |

И есть результат, который мне нужен:

[
    {"2021-12-09": {"external_events": 1, "internal_events": 1}},
    {"2021-12-10": {"external_events": 0, "internal_events": 2}},
    {"2021-12-11": {"external_events": 0, "internal_events": 1}},
    {"2021-12-13": {"external_events": 1, "internal_events": 0}},
    {"2021-12-14": {"external_events": 2, "internal_events": 2}},
    {"2021-12-15": {"external_events": 2, "internal_events": 2}},
    {"2021-12-16": {"external_events": 2, "internal_events": 0}},
]

Итак, я хочу получить все существующие даты и для каждой даты получить количество внешних событий (где is_inner == False) и количество внутренних событий (где is_inner == True). Как я могу сделать это с помощью Django ORM или в необработанном SQL? На данный момент я пришел к

EventsList.objects.annotate(
    start_day=Cast("start", output_field=DateField())
).values("start_day").annotate(
    external_events=Count("id", filter=Q(is_inner=False)),
    internal_events=Count("id", filter=Q(is_inner=True)),
).values(
    "start_day", "external_events", "internal_events"
)

Этот код возвращает почти правильный результат (но только для "начальных" дат). Мне нужно включить все даты (начальную дату, конечную дату и все даты между ними). Буду признателен за любую помощь.

Во-первых, чтобы получить все даты, которых нет в вашей базе данных, вы должны выполнить цикл между максимальной и минимальной датой, чтобы получить все даты и для каждой даты выполнить запросы, соответствующие фильтрам, которые вы хотите.

Используя необработанный SQL, сначала расширьте список дат с помощью generate_series и lateral join, а затем выполните условную агрегацию. Итак, вот он, немного многословный, но, надеюсь, легко читаемый. SQL Fiddle

with t as
(
 select is_inner, d::date from the_table 
 cross join lateral generate_series
 (
  date_trunc('day', "start"), 
  date_trunc('day', "end"),  
  interval '1 day'
 ) as d
)
select d as event_date, 
       count(*) filter (where not is_inner) external_events,   
       count(*) filter (where is_inner) internal_events
from t
group by d order by d; 

Вы можете сформировать структуру JSON в вопросе, используя jsonb_build_object следующим образом:

with t as
(
 select is_inner, d::date from the_table 
 cross join lateral generate_series
 (
  date_trunc('day', "start"), 
  date_trunc('day', "end"),  
  interval '1 day'
 ) as d
)
select jsonb_build_object
(
  d::text, 
  jsonb_build_object('external_events', count(*) filter (where not is_inner),   
                     'internal_events', count(*) filter (where is_inner))
) as date_info   
from t
group by d order by d; 
date_info
{"2021-12-09": {"external_events": 1, "internal_events": 1}}
{"2021-12-10": {"external_events": 1, "internal_events": 2}}
{"2021-12-11": {"external_events": 0, "internal_events": 1}}
{"2021-12-13": {"external_events": 1, "internal_events": 0}}
{"2021-12-14": {"external_events": 2, "внутренние_события": 2}}
{"2021-12-15": {"external_events": 2, "internal_events": 2}}
{"2021-12-16": {"external_events": 2, "internal_events": 0}}
Вернуться на верх