Вложенные подзапросы в Django
Попасть в глубокую воду с помощью Subquery. У меня есть набор Carpark
s. Carpark
с несколькими Booking
s. Заказы имеют много BarrierActivity
записи, которые представляют собой различные приходящие и уходящие события на барьерах. Это все простые ФК в стеке.
Возможно прибытие заказа, и барьерные камеры не распознают его. Сотрудник будет жужжать им, но это означает, что система по какой-то причине вышла из строя. И это то, что я пытаюсь сделать здесь. Определите, какой процент моих заказов поступил с помощью автоматических средств. Я знаю, что есть ряд других способов сделать это, но я хотел бы сделать это с одним набором запросов на основе подзапросов.
Моя цель достаточно проста. Аннотируйте 0 или 1, чтобы показать, является ли "запись" BarrierActivity
существует для каждого Booking
, Аннотировать среднее из этих значений, за Carpark
,
Первая часть в порядке. Я могу сделать простой Exists()
между BarrierActivity
а также Booking
и тогда каждое бронирование имеет 0 или 1:
successful_bas = BarrierActivity.objects.order_by().filter(
booking=OuterRef('pk'),
activity_type=BarrierActivity.TYPE_ANPR_BOOKING,
direction='entry'
).values('booking')
Booking.objects.order_by().annotate(
entry_success=Exists(successful_bas)
)
И снова, это работает отлично. Но как только я пытаюсь масштабировать это еще один слой (так что глядя на Carpark
вместо Booking
)...
successful_bas = BarrierActivity.objects.order_by().filter(
booking=OuterRef('pk'),
activity_type=BarrierActivity.TYPE_ANPR_BOOKING,
direction='entry'
).values('booking')
bookings = Booking.objects.order_by().filter(
carpark=OuterRef('pk')
).values('carpark').annotate(
entry_success=Exists(successful_bas)
).values('entry_success')
Carpark.objects.order_by().annotate(
entry_hitrate=ExpressionWrapper(
Avg(Cast(Subquery(bookings), IntegerField())) * 100,
output_field=FloatField()
)
)
... Я получаю Subquery-error-of-doom: more than one row returned by a subquery used as an expression
, bookings
подзапрос явно возвращает слишком много, но как мне скомбинировать это, прежде чем он попадет в самый внешний подзапрос?
Я перепробовал много вещей, но вот реорганизация среднего в подзапрос. Та же ошибка:
successful_bas = "... as before ..."
bookings = Booking.objects_standard.order_by().filter(
carpark=OuterRef('pk')
).values('site').annotate(
entry_success=Exists(successful_bas)
).annotate(
entry_avg=Avg(Cast('entry_success', IntegerField()))
).values('entry_avg')
Carpark.objects.order_by().annotate(
entry_hitrate=ExpressionWrapper(
Subquery(bookings, output_field=FloatField()) * 100,
output_field=FloatField()
)
)
1 ответ
Я смог воссоздать часть этого в одном из моих собственных проектов и добавить distinct('<values_field_name>').order_by()
чтобы внешний подзапрос решил это.
Отдельный вызов необходим для уменьшения количества возвращаемых строк. Имя поля в отдельном вызове требуется, в противном случае оно пытается выполнить отдельный вызов через другое поле. Тогда order_by()
call очищает любой порядок, так что запрос имеет порядок в отдельном выражении, соответствующий порядку исходного выражения.
Вот что я бы попробовал:
successful_bas = BarrierActivity.objects.order_by().filter(
booking=OuterRef('pk'),
activity_type=BarrierActivity.TYPE_ANPR_BOOKING,
direction='entry'
).values('booking')
bookings = Booking.objects.order_by().filter(
carpark=OuterRef('pk')
).annotate(
entry_success=Exists(successful_bas)
).values('carpark').distinct('carpark').order_by()
Carpark.objects.order_by().annotate(
entry_hitrate=ExpressionWrapper(
Avg(Cast(Subquery(bookings), IntegerField())) * 100,
output_field=FloatField()
)
)
Обратите внимание, что я удалил один из values
вызовите внешний подзапрос.