Найдите ближайшую запись не позже даты и ближайшую не раньше неё, затем сравните два расстояния. Индекс по datetime позволяет избежать сортировки всей таблицы по вычисляемой разнице.
Определение ближайшего курса валюты по указанной дате
Найдите ближайшую запись не позже даты и ближайшую не раньше неё, затем сравните два расстояния. Индекс по datetime позволяет избежать сортировки всей таблицы по вычисляемой разнице.
Короткий ответ
Что ответить на собеседовании
Подробный разбор
Ответ с пояснениями
Условие
Необходимо реализовать API‑endpoint, принимающий дату от клиента и возвращающий запись курса валюты, ближайшую к этой дате из таблицы CurrencyRate. Если в базе существует точное совпадение по полю datetime, его следует вернуть; если такого значения нет – выбрать запись с минимальной разницей во времени, независимо от того, находится она раньше или позже запрошенного момента.
class CurrencyRate(models.Model):
rate = models.DecimalField(...)
datetime = models.DateTimeField(...)
+--------+----------------------------+
| rate | datetime |
+--------+----------------------------+
| 34.9 | 1999-05-21 15:44:12.983411 |
| 70.3 | 2022-12-20 08:30:16.123351 |
| 68.1 | 2023-01-09 10:13:30.431559 |
| 68.2 | 2023-01-10 10:00:12.123471 |
+--------+----------------------------+
import datetime as dt
def index(request):
my_dt: dt.datetime = get_dt_from_request(request)
...
Ответ
Найдите ближайшую запись не позже даты и ближайшую не раньше неё, затем сравните два расстояния. Индекс по datetime позволяет избежать сортировки всей таблицы по вычисляемой разнице.
После валидации даты и согласования часового пояса:
from django.http import JsonResponse, Http404
def index(request):
target = get_dt_from_request(request)
before = CurrencyRate.objects.filter(datetime__lte=target).order_by('-datetime', 'pk').first()
after = CurrencyRate.objects.filter(datetime__gte=target).order_by('datetime', 'pk').first()
candidates = [item for item in (before, after) if item is not None]
if not candidates:
raise Http404('No rates')
nearest = min(candidates, key=lambda item: (
abs(item.datetime - target), item.datetime, item.pk
))
return JsonResponse({'rate': str(nearest.rate), 'datetime': nearest.datetime.isoformat()})
При равенстве расстояний выбирается более ранняя запись. Для строгой согласованности двух чтений при конкурентных изменениях нужен общий snapshot либо единый SQL-запрос. Если валют несколько, обязательно добавьте фильтр валюты.