Найдите ближайшую запись не позже даты и ближайшую не раньше, затем сравните две разницы. Такой подход использует индекс по datetime и не требует вычислять расстояние до каждой строки таблицы.
Нахождение курса валюты, ближайшего к заданной дате
Найдите ближайшую запись не позже даты и ближайшую не раньше, затем сравните две разницы. Такой подход использует индекс по datetime и не требует вычислять расстояние до каждой строки таблицы.
Короткий ответ
Что ответить на собеседовании
Подробный разбор
Ответ с пояснениями
Условие
Необходимо реализовать функцию, которая по переданной дате возвращает курс валюты, записанный в базе в момент, наиболее близкий к этой дате. В качестве примера используется Django‑модель CurrencyRate, содержащая поля rate (Decimal) и datetime (DateTime). В таблице могут быть записи за разные годы, а запрос должен подобрать значение, чей timestamp минимально отклоняется от запрошенного.
# currency service
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 12:30:13.431559 |
| 68.2 | 2023-01-10 10:00:12.123471 |
# Пример преобразования
| 2020-12-20 00:00:00.000000 -> 70.3 |
| 2000-12-20 00:00:00.000000 -> 34.9 |
def handler(dt: datetime) -> Decimal:
...
Ответ
Найдите ближайшую запись не позже даты и ближайшую не раньше, затем сравните две разницы. Такой подход использует индекс по datetime и не требует вычислять расстояние до каждой строки таблицы.
При равном расстоянии выберем более раннюю запись; при пустой таблице — LookupError:
def handler(dt):
before = CurrencyRate.objects.filter(
datetime__lte=dt,
).order_by('-datetime', 'pk').first()
after = CurrencyRate.objects.filter(
datetime__gte=dt,
).order_by('datetime', 'pk').first()
candidates = [row for row in (before, after) if row is not None]
if not candidates:
raise LookupError('No currency rates')
best = min(candidates, key=lambda row: (
abs(row.datetime - dt), row.datetime, row.pk,
))
return best.rate
Уточнение модели: правильное имя поля Django — DateTimeField. Используйте согласованные timezone-aware даты. При конкурентном изменении набора курсов два запроса требуют общего снимка, если нужна строгая согласованность.