6

これが明らかな質問である場合は申し訳ありませんが、ポニーとデータベース全般に不慣れで、この質問に答えるドキュメントの適切な部分が見つかりませんでした。

企業とその企業がオフィスを構える場所のデータベースを作成しようとしています。各会社は複数の場所にあり、各場所は複数の会社のホストになる可能性があるため、これは多対多の関係です。私は自分のエンティティを次のように定義しています:

from pony import orm

class Company(db.Entity):
    '''A company entry in database'''
    name = orm.PrimaryKey(str)
    locations = orm.Set('Location')

class Location(db.Entity):
    '''A location for a company'''
    name = orm.PrimaryKey(str)
    companies = orm.Set('Company')

理想的には、会社をデータベースに追加する関数を作成し、その会社が存在する場所のリストを追加し、まだ存在しない場合は新しい場所のインスタンスを確実に追加できるようにしたいと考えています。そのための 2 つの方法をすぐに思いつくことができます。

最初に、場所が存在する場合でもその場所を入力して、例外を処理しようとします。

@orm.db_session
def add_company(name, locations):
    loc_entities = []
    for l in locations:
        try:
            loc = Location[l]
        except orm.core.ObjectNotFound:
            loc = Location(name=l)
        else:
            loc_entities.append(loc)
    comp = Company(name=name, locations=loc_entities)

2 つ目は、データベースにクエリを実行し、場所がまだ存在するかどうかを確認することです。

@orm.db_session
def add_company2(name, locations):
    old_loc_entities = orm.select(l for l in Location if l.name in locations)[:]
    old_locations = [l.name for l in old_loc_entities]
    new_locations = set(locations) - (set(locations) & set(old_locations))
    loc_entities = [Location(name=l) for l in new_locations] + old_loc_entities
    comp = Company(name=name, locations=loc_entities)

これら2つのうち、よりpythonicな方法は単に例外を処理することだと思いますが、これはN + 1の問題に遭遇しますか? 名前を主キーとして使用することで、インデックスを使用してエンティティにアクセスするたびにクエリを作成していることに気付きました。ポニーにシーケンシャル ID を選択させるだけでは、クエリを実行する必要はないようです。大規模なデータセットでこれをまだテストしていないため、まだベンチマークしていません。

4

2 に答える 2

5

名前を主キーとして使用することで、インデックスを使用してエンティティにアクセスするたびにクエリを作成していることに気付きました。ポニーにシーケンシャル ID を選択させるだけでは、クエリを実行する必要はないようです。

内部的には、Pony は連続する主キーを文字列の主キーと同じようにキャッシュするので、違いはないと思います。それぞれdb_sessionに個別のキャッシュがあります (「ID マップ」と呼ばれます)。オブジェクトが読み取られた後、同じオブジェクト内の主キー (またはその他の一意のキー) によるアクセスはdb_session、新しいクエリを発行することなく、ID マップから直接同じオブジェクトを返す必要があります。が終了した後、db_session同じキーによる別のアクセスによって新しいクエリが発行されます。これは、同時トランザクションによってデータベース内のオブジェクトが変更される可能性があるためです。

あなたのアプローチに関しては、どちらも有効だと思います。会社の場所がほんの少し (たとえば、約 10 か所) しかない場合、私は最初のアプローチを使用します。確かに N+1 クエリが発生しますが、主キーでオブジェクトを取得するクエリは、サーバーにとって非常に高速で簡単に実行できます。getメソッドを使用すると、コードをもう少しコンパクトに表現できます。

@orm.db_session
def add_company(name, locations):
    loc_entities = [Location.get(name=l) or Location(name=l)
                    for l in locations]
    comp = Company(name=name, locations=loc_entities)

1 つのクエリですべての既存の場所を取得する 2 番目のアプローチは、私には時期尚早の最適化のように見えますが、1 秒間に数百の会社を作成し、各会社に数百の場所がある場合は、使用できる可能性があります。

于 2016-10-30T12:49:35.747 に答える