Как использовать 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()
для достижения JOIN
s.