140

私は新しいアプリケーションを開始し、ORM、特にSQLAlchemyの使用を検討しています。

データベースに列「foo」があり、それをインクリメントしたいとします。ストレートsqliteでは、これは簡単です。

db = sqlite3.connect('mydata.sqlitedb')
cur = db.cursor()
cur.execute('update table stuff set foo = foo + 1')

私はSQLAlchemySQL-builderに相当するものを見つけました:

engine = sqlalchemy.create_engine('sqlite:///mydata.sqlitedb')
md = sqlalchemy.MetaData(engine)
table = sqlalchemy.Table('stuff', md, autoload=True)
upd = table.update(values={table.c.foo:table.c.foo+1})
engine.execute(upd)

これは少し遅いですが、それほど多くはありません。

SQLAlchemyORMアプローチの私の最良の推測は次のとおりです。

# snip definition of Stuff class made using declarative_base
# snip creation of session object
for c in session.query(Stuff):
    c.foo = c.foo + 1
session.flush()
session.commit()

これは正しいことですが、他の2つのアプローチの50倍弱かかります。これは、すべてのデータを処理する前にメモリに取り込む必要があるためだと思います。

SQLAlchemyのORMを使用して効率的なSQLを生成する方法はありますか?または他のPythonORMを使用していますか?それとも、SQLを手作業で作成することに戻る必要がありますか?

4

6 に答える 6

206

SQLAlchemy の ORM は、SQL レイヤーを非表示にするのではなく、一緒に使用することを意図しています。ただし、同じトランザクションで ORM とプレーン SQL を使用する場合は、1 つまたは 2 つのことを覚えておく必要があります。基本的に、ORM データの変更は、セッションから変更をフラッシュするときにのみデータベースにヒットします。一方、SQL データ操作ステートメントは、セッション内のオブジェクトには影響しません。

だからあなたが言うなら

for c in session.query(Stuff).all():
    c.foo = c.foo+1
session.commit()

データベースからすべてのオブジェクトを取得し、すべてのオブジェクトを変更してから、変更をデータベースにフラッシュするときに行を 1 つずつ更新します。

代わりに、これを行う必要があります。

session.execute(update(stuff_table, values={stuff_table.c.foo: stuff_table.c.foo + 1}))
session.commit()

これは予想どおり 1 つのクエリとして実行されます。少なくともデフォルトのセッション構成では、コミット時にセッション内のすべてのデータが期限切れになるため、古いデータの問題はありません。

ほぼリリースされた 0.5 シリーズでは、この方法を使用して更新することもできます。

session.query(Stuff).update({Stuff.foo: Stuff.foo + 1})
session.commit()

これは基本的に前のスニペットと同じ SQL ステートメントを実行しますが、変更された行を選択し、セッション内の古いデータを期限切れにします。更新後にセッション データを使用していないことがわかっている場合はsynchronize_session=False、更新ステートメントに追加して、その選択を取り除くこともできます。

于 2008-11-10T17:40:42.797 に答える
112
session.query(Clients).filter(Clients.id == client_id_list).update({'status': status})
session.commit()

これを試してください=)

于 2010-12-27T16:28:03.490 に答える
2

オブジェクトの作成に関するオーバーヘッドが原因である場合、SA ではおそらくまったく高速化できません。

関連するオブジェクトをロードしていることが原因である場合は、遅延ロードで何かを実行できる可能性があります。参照のために作成されているオブジェクトがたくさんありますか? (つまり、Company オブジェクトを取得すると、関連する People オブジェクトもすべて取得されます)。

于 2008-11-07T01:08:04.187 に答える
1

十分なテストをせずに、私は試してみます:

for c in session.query(Stuff).all():
     c.foo = c.foo+1
session.commit()

(IIRC、commit()はflush()なしで機能します)。

大規模なクエリを実行してからPythonで反復処理する方が、多くのクエリよりも最大2桁高速になる場合があることがわかりました。クエリオブジェクトを反復処理する方が、クエリオブジェクトのall()メソッドによって生成されたリストを反復処理するよりも効率が悪いと思います。

[以下のコメントに注意してください-これは物事をまったくスピードアップしませんでした]。

于 2008-11-07T00:35:19.697 に答える