Как использовать Django ORM в объединении с агрегированным подзапросом

У меня есть такая модель (упрощенная):

class Events(models.Model):
    reference_date = models.DateField()
    event = models.IntegerField(db_index=True)
    created_at = models.DateTimeField()
    updated_at = models.DateTimeField()

    class Meta:
        unique_together = (('reference_date', 'event'),)

Я могу иметь один и тот же event с несколькими датами ссылки, но у меня есть только один event на reference_date. Данные таблицы выглядят примерно так:

id reference_date event created_at updated_at
1 2022-01-01 12345 2022-03-05 18:18:03 2022-03-06 18:12:09
2 2022-01-02 12345 2022-03-08 08:05:11 2022-03-08 08:05:55
3 2022-01-08 12345 2022-06-15 18:18:12 2022-06-16 02:23:11
4 2022-01-01 98765 2022-01-11 07:55:25 2022-01-13 08:45:12
5 2022-01-02 98765 2022-06-22 10:25:08 2022-07-05 18:55:08
6 2022-01-09 45678 2022-02-19 12:55:07 2022-04-16 12:21:05
7 2022-01-10 45678 2022-03-05 11:23:45 2022-03-05 18:55:03

Мне нужна последняя запись для каждого event. Но мне нужны все атрибуты event, а не только max(reference_date)

Я ищу этот результат:

[
{'id': 3, 'event': 12345, 'reference_date': '2022-01-08', 'created_at': '2022-06-15 18:18:12', 'updated_at': '2022-06-16 02:23:11'},
{'id': 5, 'event': 98765, 'reference_date': '2022-01-02', 'created_at': '2022-06-22 10:25:08', 'updated_at': '2022-07-05 18:55:08'},
{'id': 7, 'event': 45678, 'reference_date': '2022-01-10', 'created_at': '2022-03-05 11:23:45', 'updated_at': '2022-03-05 18:55:03'}
]

С "sql-перспективы" я мог бы получить результаты несколькими способами: SUBQUERIES, ROW_NUMBER, CORRELATED SUBQUERY и т.д. В данном конкретном случае по причинам ясности я предпочитаю использовать объединение с самим собой, используя агрегат внутри подзапроса.

Если бы я писал необработанный запрос, я бы сделал так:

SELECT
 e.*
FROM events as e
INNER JOIN (
        SELECT
         event,
         max(reference_date) as reference_date
        FROM events
        GROUP BY event) AS b
ON b.reference_date = e.reference_date AND b.event = e.event

Я думаю, что этот запрос очень прост и имеет хорошую производительность для объема данных в этой таблице (сотни тысяч записей)

Я искал несколько способов построения такого типа запросов (Subquery, PrefetchRelated и т.д.), но не смог найти никакого подходящего способа перевести этот запрос в синтаксис Django ORM.

Есть ли способ использовать 'django-orm way' без сложного ORM решения с такой же разумной производительностью?

ps: Мне нужен этот запрос (или похожие) для различных движков баз данных (PostgreSql, MySQL, MSQLServer ...)

Я не уверен на 100%, что это будет работать, в основном потому, что у меня не было необходимости (пока) использовать это с ORM Django, но что-то вроде этого может сработать. Вам нужно импортировать Max из django.db.models.

Я опубликую это и прочитаю в документации о .aggregate() - прочитал и все еще не эксперт :).

from django.db.models import Max
#Events.objects.all() -> get all the objects from Events
#.group_by("event") tells them to be grouped by the "event" column
#.aggregate(Max("reference_data")) is what I am uncertain about...
Events.objects.all().group_by("event").aggregate(Max("reference_date"))

Также есть возможность написать свой необработанный оператор для каждого движка базы данных и использовать метод .raw(query) в вашей модели для запроса к базе данных. Поскольку вам нужна поддержка нескольких баз данных, вы также можете использовать this для определения базы данных, которую вы используете для выполнения запроса.

Я также наткнулся на эту статью, которая повлияла на предложение ниже:

Events.objects.values('event').annotate(Max('reference_date'))

Последнее сообщение SO с относительно таким же вопросом упоминает использование select_related() для достижения JOINs.

Вернуться на верх