Выбор ответа Senior Django ORM и N+1 обновлено

Задвоение сумм при нескольких annotate

Вопрос

Customer.objects.annotate(orders=Count("order"), paid=Sum("payment__amount")) даёт завышенные суммы оплат. Почему?

Попробуйте сами

Выберите вариант, и откроется разбор.

Показать ответ и разбор

Коротко

Правильный ответ: Два JOIN «ко многим» перемножают строки, и оплаты считаются повторно. Django строит один запрос с JOIN к заказам и к оплатам.

Варианты ответа

  1. У Sum ошибка округления при суммировании Decimal в PostgreSQL
  2. Count и Sum нельзя использовать в одном annotate, нужен второй вызов
  3. Нужно добавить order_by, иначе группировка идёт по случайному полю
  4. Два JOIN «ко многим» перемножают строки, и оплаты считаются повторноправильный ответ

Разбор

Django строит один запрос с JOIN к заказам и к оплатам. Для клиента с 3 заказами и 2 оплатами получится 6 строк, и сумма оплат утроится. Count(..., distinct=True) спасает подсчёт, но не сумму. Надёжное решение: подзапросы через Subquery и OuterRef с агрегацией или отдельные запросы. order_by и округление тут ни при чём.

Ответьте сами, а не прочитайте

В тренажёре вопрос попадёт в повторения и вернётся, когда начнёт забываться. Плюс билеты с зачётом: 20 вопросов, не больше 2 ошибок.