2

Django ORMでサブクエリを作成するのは簡単です(QuerySet別のクエリの一部としてを使用するだけです)が、そのサブクエリが「親」(外部、メイン)クエリのフィールドを参照することは可能ですか?

私が達成しようとしていることの完全な例については、この動作するSQLFiddleを参照してください。私はそれを2つの質問に分けました(他の1つはここにあります)。この場合、Whole到達しなければならない値を表すモデルがあります。いくつかPartのは、独自の(計算された)値でそれに貢献します。まだ完了していない(つまり、Whole個々total_value値の合計とは異なる)すべてのを取得したい。

select w.*
  from whole w
  where w.total_value != (
    select sum(value expression)
      from part p
      where p.whole_id = w.id
      group by p.whole_id
  );

Django ORMを使用してこれを行う方法(または可能かどうか)がわかりません。を使用したサブクエリの例をたくさん 見てきましたが(結果が実際に単一のクエリとして実行されていることを確認できました)、両方のクエリが互いに独立している場合に限ります。ここで、サブクエリは親クエリ()のフィールドによって制約されます。、、、またはを使用することを考えましたが、何をすべきかを完全に理解することはできません...__inprint qs.queryw.idF()Q()extra

誰かがそれを実験したい場合に備えて、これがSSCCEです:ダウンロードまたは参照。上記でリンクされているSQLフィドルと同じモデルとデータがあります。


更新:私の特定のケースでは、サブクエリを実行する必要がないことがわかりました。使用するだけgroup byhavingこのSQL Fiddleが示すように):

q = Q(part__isnull=True) | ~Q(partial=F('total_value'))
qs = Whole.objects.annotate(partial=Sum(...)).filter(q).distinct()

# And if total_value can be zero:
qs = qs.exclude(part__isnull=True, total_value=0)

ただし、サブクエリの一般的なケースはまだ解決されていません(以下の私の答えが示すように、生のSQLを使用することはできません)。

4

1 に答える 1

2

生のSQLの使用を最小限に抑えて考案したソリューションextrawhere

  • まず、内部クエリを作成します。extraカスタムコンポーネントを指定するために使用しwhere、制限されたフィールドを外部クエリのフィールドと比較します(テーブル名/エイリアスをハードコーディングする必要がある場合があります)

    qs1 = Part.objects.extra(where=['whole_id = "applabel_whole"."id"'])...
    

    次に、残りの操作を行います(この場合、グループ化、集約、および単一フィールドの戻りを使用して)valuesannotate

  • 次に、(を使用して.query)内部クエリの生成されたSQLを外部クエリに含めます。また、extraおよびwhere:を使用します。

    qs = Whole.objects.extra(where=['total_value != ({})'.format(qs1.query)])
    

呼び出しのコードフラグメントはextra移植可能ではない可能性があります(例:一部のバックエンドが使用する!=、他が使用する<>、テーブル名を引用する正しい方法が異なる可能性があるなど)が、内部クエリの残りの部分は移植可能である必要があります( ORM)。

結果のクエリは、私が探しているものに対応します(他の質問でカバーされている集計部分を除く)。読みやすさのためにフォーマットされたSQL:

>>> qs1 = Part.objects.extra(
        where=['whole_id = "aggregation_subquery_whole"."id"']
    ).values('whole_id').annotate(sum=Sum('before__value')).values('sum')

>>> qs = Whole.objects.extra(where=['total_value != ({})'.format(qs1.query)])

>>> print qs.query

SELECT "aggregation_subquery_whole"."id",
       "aggregation_subquery_whole"."total_value" 
FROM "aggregation_subquery_whole"
WHERE total_value != (
    SELECT SUM("aggregation_subquery_sequence"."value") AS "sum"
    FROM "aggregation_subquery_part"
        LEFT OUTER JOIN "aggregation_subquery_sequence" ON
           ("aggregation_subquery_part"."before_id" =
            "aggregation_subquery_sequence"."id") 
    WHERE whole_id = "aggregation_subquery_whole"."id"
    GROUP BY "aggregation_subquery_part"."whole_id"
)
于 2013-03-18T17:57:43.563 に答える