Вложенные подзапросы в Django

Попасть в глубокую воду с помощью Subquery. У меня есть набор Carparks. Carparkс несколькими Bookings. Заказы имеют много 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 вызовите внешний подзапрос.

Другие вопросы по тегам