2017-06-07 17 views
0

サブクエリで深い水に入る。私はCarparkのセットを持っています。 Carparkに複数のBookingがあります。予約には、障壁での様々な行き来するイベントである多くのBarrierActivityレコードがあります。これらはすべてスタック上の単純なFKです。Djangoのネストされたサブクエリ

予約は到着することができ、バリアカメラではありません。スタッフの一員がそれを鳴らしますが、それは何らかの理由でシステムが失敗したことを意味します。それが私がここでやろうとしていることです。私の予約の何パーセントが自動化された手段で取り込まれたかを調べる。私はこれを行う他の多くの方法があることを知っていますが、私は単一のサブクエリベースのクエリセットでこれをやりたいと思います。

私の目的はかなり簡単です。各Bookingに「エントリ」BarrierActivityが存在するかどうかを示す0または1の注釈を付けます。これらの値の平均値をCarparkに注記してください。

最初の部分は問題ありません。私はBarrierActivityBooking間の単純なExists()を行うことができますし、各予約が0または1を持っています

そして再び、正常に動作しています。しかし、すぐに、私は別の層(そう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() 
    ) 
) 

ていることにスケールアップしようとして...私はサブクエリ誤りの-運命を得る:more than one row returned by a subquery used as an expressionbookingsサブクエリーは明らかにあまりにも多くを返しますが、最も外側のサブクエリに当たる前にそれをどのように集計しますか?


私は多くのことを試しましたが、ここではサブクエリ内の平均値を再編成しています。同じエラー:

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() 
    ) 
) 
+0

サブクエリが複数返されています。キャストを呼び出す前に平均を取る – Brobin

+0

キャストはAvgが動作するために必要です(bool→intを実行しています)。私はいくつかの選択肢を試してみました - 良い効果はありません - あなたが提案しているもののように編集します。 – Oli

+0

@Oliあなたのアプローチが不必要に複雑すぎるように見える(boolをintにキャストしてから平均していくのか?)、単純なアプローチで簡単なアプローチを見つけることができます。 – Todor

答えて

0

私は自分のプロジェクトの一つで、このの再作成部分にできたし、それを解決し、外側のサブクエリにdistinct('<values_field_name>').order_by()を追加します。

返される行数を減らすには、別個の呼び出しが必要です。別のコールのフィールド名が必要です。それ以外の場合は、別のフィールドを介して別のコールを実行しようとします。次に、order_by()呼び出しは、最初の式の順序に一致する別個の式の順序が問合せになるように、すべての順序を消去します。

これは私が試してみたものです:私は、外側のサブクエリでvaluesコールのいずれかを削除

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() 
    ) 
) 

注意。

+0

いくつかの問題があります。そのまま、駐車場IDを返します。最後のvalues()呼び出しを 'entry_success'に変更すると、それは最初のものか、それとも何かを選ぶようです。それはゼロを返す。私はいくつかの新しいBarrierActivityレコードを詰め込み、ゼロを返さないようにしました。 – Oli

+0

私は、あなたの特定のケースが私のテストケースとあまり変わっていないことを恐れていました。ごめんなさい。クエリを印刷して、ORMソリューションのリバースエンジニアリングを試してみてください。 – schillingt

関連する問題