Какова наилучшая практика работы с несколькими агрегациями базы данных и пагинацией

У меня есть REST сервис (проект использует django и DRF), который обслуживает данные из базы данных MSSQL. Одной из моделей, хранящихся в БД, являются инвойсы, которые выглядят следующим образом:

class Invoice(models.Model):
   id = models.IntegerField(primary_key=True)
   customer = models.ForeignKey('project_models.Customer', on_delete=models.CASCADE,related_name='invoices')
   material = models.ForeignKey('project_models.Material', on_delete=models.SET_NULL)
   quantity = models.FloatField()
   revenue = models.FloatField()
   invoice_date = models.DateField()

Я хочу представить некоторые агрегированные данные для этих счетов-фактур по материалам, например:

  • Сумма выручки за текущий год
  • Сумма выручки за прошлый год
  • Сумма выручки за прошлый год до сегодняшней даты (например, сегодня 2022-03-02, сумма будет с 2021-01-01 по 2021-03-01)

Для решения этой проблемы я могу выполнить запрос с минимальной агрегацией (например, сумма выручки за день) и итерацией по набору результатов для создания списка записей, который содержит всю необходимую информацию. Однако, поскольку материалов много, такое решение может привести к проблемам с производительностью. Обычно эта проблема решается путем постраничной обработки набора запросов. Поскольку я все еще выполняю выборку и вычисления, а также итерации по всем данным БД, это кажется не самым лучшим решением.

Поэтому мой вопрос: что было бы лучшим подходом для объединения нескольких данных, сохраняя при этом пагинацию.

Если вас беспокоит производительность при получении информации о счете-фактуре и его связях (клиент, материал), вы можете взглянуть на select_related и prefetch_related.

Оба направлены на повышение производительности за счет уменьшения количества обращений к базе данных.

select_related

select_related собирает в одном вызове базы данных поиск информации об объекте (в данном случае количество, выручка, ...) и о его связях. Объединение таблиц производится на уровне базы данных (фактически в SQL). Оно работает только с отношениями с иностранными ключами.

prefetch_related

prefetch_related делает один вызов для объекта (Invoice в нашем случае), и один вызов на отношение (customer и material в нашем случае). Соединение таблиц выполняется на уровне Python. Оно работает с отношениями "многие ко многим" и "многие к одному".

Выполнение

Поэтому вам следует просто переписать запрос, оставив вычисления нетронутыми (сумма выручки за текущий год, ...). Учитывая threshold_date, запрос выглядит следующим образом :

invoices = Invoice.objects.filter(invoice_date__gt=threshold_date).prefetch_related('customer', 'material')

Однако, если вас интересует только вычисление результата, а не извлечение информации об объектах (для счета, клиента или материала), я бы рекомендовал выполнять вычисления на уровне базы данных.

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