Django ORM:多対多関係を含むクエリの最適化

django django-models django-queryset many-to-many orm
Django ORM:多対多関係を含むクエリの最適化

次のようなモデル構造があります。

class Container(models.Model):
    pass

class Generic(models.Model):
    name = models.CharacterField(unique=True)
    cont = models.ManyToManyField(Container, null=True)
    # It is possible to have a Generic object not associated with any container,
    # thats why null=True

class Specific1(Generic):
    ...

class Specific2(Generic):
    ...

...

class SpecificN(Generic):
    ...

たとえば、特定のコンテナと関係があるすべての `Specific`型モデルを取得する必要があります。

そのためのSQLは、多かれ少なかれ些細なことですが、それは問題ではありません。 残念ながら、私はORM(特にDjangoのORM)を使った作業についてはあまり経験がないので、ここではパターンを見逃しているかもしれません。

ブルートフォース方式で行われると、 –

c = Container.objects.get(name='somename') # this gets me the container
items = c.generic_set.all()
# this gets me all Generic objects, that are related to the container
# Now what? I need to get to the actual Specific objects, so I need to somehow
# get the type of the underlying Specific object and get it
for item in items:
    spec = getattr(item, item.get_my_specific_type())

その結果、1トンのdbヒットが発生します(各汎用レコードに1つ、コンテナーに関連します)。したがって、これは明らかに不可能です。 これで、おそらく、SpecificXオブジェクトを直接取得することでそれを実行できます。

s = Specific1.objects.filter(cont__name='somename')
# This gets me all Specific1 objects for the specified container
...
# do it for every Specific type

そのようにしてdbは各Specific typeに対して1回ヒットするでしょう(許容できる、私は思います)。

私は、.select_related()がm2mリレーションシップでは機能しないことを知っています、それでそれはここであまり役に立ちません。

繰り返しますが、最終結果はSpecificXオブジェクトのコレクションになります(Genericではなく)。

  5  1


ベストアンサー

私はあなたがすでに2つの簡単な可能性を概説したと思います。 Genericに対して単一のフィルタクエリを実行してから各項目をその特定のサブタイプにキャストする(結果はn 1クエリで、nは返される項目の数です)か、特定の各テーブルに対して個別のクエリを実行しますここで、kは特定の型の数です。

実際にこれらのうちどれが速いかを見ることは実際にベンチマークをとる価値があります。 2番目のクエリは(おそらく)クエリが少ないのでより優れているように見えますが、それらのクエリはそれぞれm2m中間テーブルとの結合を実行する必要があります。 前者の場合、結合クエリを1回だけ実行してから、単純なものを多数実行します。 いくつかのデータベースバックエンドは、より少ない、より複雑なものよりも、たくさんの小さなクエリを使用した方が良いパフォーマンスを示します。

2つ目が実際にあなたのユースケースのためにかなり速い、そしてあなたがあなたのコードをきれいにするためにいくらかの追加の仕事をしても構わないと思っているならば、サブタイプ表ごとに1つの照会のみを使用して、特定の照会セットに関連する特定の表からのサブタイプ・データ。 このスニペットが一括プリフェッチを使って一般的な外部キーを最適化する方法と似ています。 これにより、最初のオプションのDRYer構文を使用して、2番目のオプションと同じクエリが得られます。

2


完全な答えではありませんが、こうすることで大量のヒットを避けることができます。

items = items内のitemのリスト(items):spec = getattr(item、item.get_my_specific_type())

これの代わりに :

アイテム内のアイテムの場合:spec = getattr(item、item.get_my_specific_type())

確かに、pythonリストへのキャストを強制することによって、あなたはdjango組織にあなたのクエリセットの全ての要素をロードさせるように強制します。 それはそれから1つの質問でこれをする。

1


私は誤って次の投稿に行き詰まりました。これはほとんどあなたの質問に答えます:

0


タイトルとURLをコピーしました