При агрегировании SQL требует, чтобы любое значение в поле было либо уникальным в группе, либо чтобы поле было включено в функцию агрегирования, которая гарантирует, что для каждой группы будет получено только одно значение. Проблема здесь в том, что «app_plan.cap» может иметь много разных значений для каждой комбинации «app_phone.id» и «app_phone.plan_id», поэтому вам нужно указать БД, как их обрабатывать.
Итак, допустимый SQL для вашего результата - одна из двух разных возможностей, в зависимости от того, какой результат вы хотите. Во-первых, вы можете включить app_plan.cap
в функцию GROUP BY, чтобы любая отдельная комбинация (app_phone.id, app_phone.plan_id, app_plan.cap) была другой группой:
SELECT "app_phone"."id", "app_phone"."plan_id", "app_plan"."cap",
SUM("app_call"."cost") AS "total_cost"
FROM "app_phone"
INNER JOIN "app_plan" ON ("app_phone"."plan_id" = "app_plan"."id")
LEFT OUTER JOIN "app_call" ON ("app_phone"."id" = "app_call"."phone_id")
GROUP BY "app_phone"."id", "app_phone"."plan_id", "app_plan"."cap"
HAVING SUM("app_call"."cost") >= 0.5 * "app_plan"."cap"
Хитрость заключается в том, чтобы получить дополнительное значение в вызове "GROUP BY". Мы можем облегчить это путем злоупотребления «extra», хотя это жестко закодирует имя таблицы для «app_plan», которая неидеальна - вы можете сделать это программно с классом Plan, если хотите:
Phone.objects.extra({
"plan_cap": "app_plan.cap"
}).annotate(
total_cost=Sum('calls__cost')
).filter(total_cost__gte=0.5*F('plan__cap'))
В качестве альтернативы, вы можете заключить app_plan.cap
в функцию агрегирования, превратив ее в уникальное значение. Функции агрегирования различаются в зависимости от поставщика БД, но могут включать такие вещи, как AVG, MAX, MIN и т. Д.
SELECT "app_phone"."id", "app_phone"."plan_id",
SUM("app_call"."cost") AS "total_cost",
AVG("app_plan"."cap") AS "avg_cap",
FROM "app_phone"
INNER JOIN "app_plan" ON ("app_phone"."plan_id" = "app_plan"."id")
LEFT OUTER JOIN "app_call" ON ("app_phone"."id" = "app_call"."phone_id")
GROUP BY "app_phone"."id", "app_phone"."plan_id"
HAVING SUM("app_call"."cost") >= 0.5 * AVG("app_plan"."cap")
Вы можете получить этот результат в Django, используя следующее:
Phone.objects.annotate(
total_cost=Sum('calls__cost'),
avg_cap=Avg('plan__cap')
).filter(total_cost__gte=0.5 * F("avg_cap"))
Возможно, вы захотите обновить оставленный вами отчет об ошибке с более точной спецификацией ожидаемого результата - например, действительного SQL, к которому вы стремитесь.