Эффективное получение первого и последнего экземпляров модели в Django Model с меткой времени, по дням
Предположим, что у вас есть такая модель:
from django import models
from django.contrib.postgres.indexes import BrinIndex
class MyModel(model.Models):
device_id = models.IntegerField()
timestamp = models.DateTimeField(auto_now_add=True)
my_value = models.FloatField()
class Meta:
indexes = (BrinIndex(fields=['timestamp']),)
Существует периодический процесс, который создает экземпляр этой модели каждые 2 минуты или около того. Предполагается, что этот процесс будет работать годами, с множеством устройств, поэтому эта таблица будет содержать большое количество записей.
Моя цель - для каждого дня, когда есть записи, получить первую и последнюю записи за этот день.
Пока что я смог придумать следующее:
from django.db.models import Min, Max
results = []
device_id = 1 # Could be other device id, of course, but 1 for illustration's sake
# This will get me a list of dictionaries that have first and last fields
# with the desired timestamps, but not the field my_value for them.
first_last = MyModel.objects.filter(device_id=device_id).values('timestamp__date')\
.annotate(first=Min('timestamp__date'),last=Max('timestamp__date'))
# So now I have to iterate over that list to get the instances/values
for f in first_last:
first = f['first']
last = f['last']
first_value = MyModel.objects.get(device=device, timestmap=first).my_value
last_value = MyModel.objects.get(device=device, timestamp=last).my_value
results.append({
'first': first,
'last': last,
'first_value': first_value,
'last_value': last_value,
})
# Do something with results[]
Это работает, но занимает много времени (около 50 секунд на моей машине, извлекая первые и последние значения для примерно 450 дней).
Я пробовал другие комбинации annotate(), values(), values_list(), extra() и т.д., но это лучшее, что я смог придумать на данный момент.
Любая помощь или понимание будут оценены по достоинству!
Вы можете воспользоваться преимуществами .distinct()
, если вы используете PostgreSQL в качестве СУБД.
first_models = MyModel.objects.order_by('timestamp__date', 'timestamp').distinct('timestamp__date')
last_models = MyModel.objects.order_by('timestamp__date', '-timestamp').distinct('timestamp__date')
first_last = first_models.union(last_models)
# do something with first_last
Необходимо упомянуть еще одну вещь: first_last
может исключить дублирование, если для даты существует только одна запись. Это не должно быть проблемой для вас, но если это произойдет, вы можете выполнить итерации first_models
и last_models
отдельно.