web-dev-qa-db-ja.com

SQLAlchemyフィルターのin_演算子

次のように、sqlalchemyのクエリで簡単なフィルター操作を実行しようとしています。

_q = session.query(Genotypes).filter(Genotypes.rsid.in_(inall))
_

どこ

inallは、Genotypesがテーブルにマップされている文字列のリストです:クラスGenotypes(object):pass

_Genotypes.mapper = mapper(Genotypes, kg_table, properties={'rsid': getattr(kg_table.c, 'rs#')})
_

これは私には簡単に思えますが、q.first()を実行して上記のクエリを実行すると、次のエラーが発生します。

"sqlalchemy.exc.OperationalError:(OperationalError)SQL変数が多すぎるu'SELECT"に続いて、inallリスト内の1Mアイテムのリスト。しかし、それらはSQL変数ではなく、メンバーシップがフィルタリング基準であるリストにすぎません。

フィルタリングを正しく行っていませんか?

(dbはsqliteです)

18
user1988705

rsidsを取得するテーブルが同じデータベースで使用可能な場合、 subquery を使用して、それらをGenotypesクエリに渡すのではなく、 Pythonコード内の100万エントリ。

sq = session.query(RSID_Source).subquery()
q = session.query(Genotypes).filter(Genotypes.rsid.in_(sq))

問題は、そのリストをSQLite(または実際には任意のデータベース)に渡すために、SQLAlchemyがin句の各エントリを変数として渡す必要があることです。 SQLはおおよそ次のように変換されます。

-- Not valid SQLite SQL
DECLARE @Param1 TEXT;
SET @Param1 = ?;
DECLARE @Param2 TEXT;
SET @Param2 = ?;
-- snip 999,998 more

SELECT field1, field2, -- etc.
FROM Genotypes G
WHERE G.rsid IN (@Param1, @Param2, /* snip */)
18
Sean Vieira

以下の回避策は私のために働きました:

q = session.query(Genotypes).filter(Genotypes.rsid.in_(inall))
query_as_string = str(q.statement.compile(compile_kwargs={"literal_binds": True}))
session.execute(query_as_string).first()

これは基本的に、実行前にクエリを文字列としてコンパイルすることを強制し、変数の問題全体をバイパスします。これに関するいくつかの詳細はSQLAlchemyのドキュメント here にあります。

ところで、SQLiteを使用していない場合は、ANY演算子を使用して、リストオブジェクトを単一のパラメーターとして渡すことができます(この質問に対する私の回答をご覧ください here )。

5
Ido S