4

sqlalchemyに、単一のクエリで宣言型のポリモーフィック結合の読み込みをオフにする方法はありますか?ほとんどの場合それは素晴らしいですが、私は持っています:

class A(Base) : 
   discriminator = Column('type', mysql.INTEGER(1), index=True, nullable=False)
   __mapper_args__ = { 'polymorphic_on' : discriminator }
   id = Column(Integer, primary_key=True)
   p = Column(Integer)

class B(A) : 
   __mapper_args__ = { 'polymorphic_identity' : 0 }
   id = Column(Integer, primary_key=True)
   x = Column(Integer)

class C(A) : 
   __mapper_args__ = { 'polymorphic_identity' : 1 }
   id = Column(Integer, primary_key=True)
   y = Column(String)

Aが実際にBである場合はBx>10、またはAが実際にCである場合はCy =='blah'であるすべてのA.idを取得するようにクエリを作成し、すべてpで並べ替えます。

繰り返し行うために、私は最初の部分から始めます-「そのAが実際にBである場合、Bx>10であるすべてのA.idを取得します」。だから私は外部結合から始めると思いました:

session.query(A.id).outerjoin((B, B.id == A.id)).filter(B.x > 10)

...ただし、outerjoin((B、B.id == A.id))句が、副選択内のAのすべてとBのすべての完全な結合を生成することを回避する方法はないようです。BがAから継承しない場合、それは発生しないので、それを行うのは多態的な宣言型コード生成だと思います。それをオフにする方法はありますか?または、outerjoinに私が望むことを強制するのですか?

私が欲しいのはこのようなものです:

select a.id from A a left outer join B b on b.id == a.id where b.x > 10

しかし、代わりに私は次のようなものを手に入れます:

select a.id from A a left outer join (select B.id, B.x, A.id from B inner join A on B.id == A.id)

...余談ですが、それが不可能な場合、後者は前者よりも効率が悪いのでしょうか。SQLエンジンは実際にその内部結合を実行しますか、それともそれを削除しますか?

4

2 に答える 2

1

期待される結果を返すように見えるouterjoin()の代わりにwith_polymorphic()を使用する必要があります。

session.query(A).with_polymorphic(B).filter(B.x > 10).all()
# BEGIN
# SELECT "A".type AS "A_type", "A".id AS "A_id", "A".p AS "A_p", "B".id AS "B_id", "B".x AS "B_x" 
# FROM "A" LEFT OUTER JOIN "B" ON "A".id = "B".id 
# WHERE "B".x > ?
# (10,)
# Col ('A_type', 'A_id', 'A_p', 'B_id', 'B_x')

に比べ:

session.query(A.id).outerjoin((B, B.id == A.id)).filter(B.x > 10)
# BEGIN
# SELECT "A".id AS "A_id" 
# FROM "A" LEFT OUTER JOIN (SELECT "A".type AS "A_type", "A".id AS "A_id", "A".p AS "A_p", "B".id AS "B_id", "B".x AS "B_x" 
# FROM "A" JOIN "B" ON "A".id = "B".id) AS anon_1 ON anon_1."A_id" = "A".id 
# WHERE anon_1."B_x" > ?
# (10,)
# Col ('A_id',)

誰かがSQLAlchemyのこのきちんとしたビットをテストしたい場合に備えて、私がこれをテストするために使用したコード:

#!/usr/bin/env python
import logging
from sqlalchemy import *
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker

Base = declarative_base()

class A(Base) :
   __mapper_args__ = { 'polymorphic_on' : discriminator }
   __tablename__ = 'A'

   id = Column(Integer, primary_key=True)
   discriminator = Column('type', Integer, index=True, nullable=False)
   p = Column(Integer)

class B(A) :
   __mapper_args__ = { 'polymorphic_identity' : 0 }
   __tablename__ = 'B'

   id = Column(Integer, ForeignKey('A.id'), primary_key=True)
   x = Column(Integer)

class C(A) :
   __mapper_args__ = { 'polymorphic_identity' : 1 }
   __tablename__ = 'C'

   id = Column(Integer, ForeignKey('A.id'), primary_key=True)
   y = Column(String)

meta = Base.metadata
meta.bind = create_engine('sqlite://')
meta.create_all()

Session = sessionmaker()
Session.configure(bind=meta.bind)
session = Session()

log = logging.getLogger('sqlalchemy')
log.addHandler(logging.StreamHandler())
log.setLevel(logging.DEBUG)

session.query(A.id).outerjoin((B, B.id == A.id)).filter(B.x > 10).all()
session.query(A).with_polymorphic(B).filter(B.x > 10).all()

これをPython2.7でSQLAlchemy0.6.4を使用して実行しました。

于 2010-09-02T19:37:09.373 に答える
1

各サブクラスのクエリを個別に作成してから、それらを結合してみることができます。クエリを実行するB.idと、SQLAlchemyは暗黙的にスーパークラスに参加し、A.id代わりに戻ります。そのため、selects forを結合するB.idC.id、単一の列のみが返されます。

>>> b_query = session.query(B.id).filter(B.x > 10)
>>> c_query = session.query(C.id).filter(C.y == 'foo')
>>> print b_query.union(c_query)
SELECT anon_1."A_id" AS "anon_1_A_id" 
FROM (SELECT "A".id AS "A_id" 
FROM "A" JOIN "B" ON "A".id = "B".id 
WHERE "B".x > ? UNION SELECT "A".id AS "A_id" 
FROM "A" JOIN "C" ON "A".id = "C".id 
WHERE "C".y = ?) AS anon_1

引き続き副選択が表示されますが、結合の「レイヤー」は1つだけです。外側の選択は、列の名前を変更するだけです。

于 2010-09-03T17:52:35.047 に答える