たとえば、私はsender
とreceiver
を持つParcel
モデルを持っています。どちらもSubject
です。 私は特定の送信者から小包を取得しようとしています。私はParcel.sender.has()
を使用したくないので、パフォーマンスのために、私の実際のテーブルが大きすぎます。 docsから複数の関係参照を持つモデルによるsqlalchemyフィルタリング
:ここ
Because has() uses a correlated subquery, its performance is not nearly as good when compared against large target tables as that of using a join.
がいっぱいペーストと、実行例である:だから
from sqlalchemy import create_engine, Column, Integer, Text, ForeignKey
from sqlalchemy.orm import sessionmaker, relationship
from sqlalchemy.ext.declarative.api import declarative_base
from sqlalchemy.orm.util import aliased
engine = create_engine('sqlite://')
Session = sessionmaker(bind=engine)
s = Session()
Base = declarative_base()
class Subject(Base):
__tablename__ = 'subject'
id = Column(Integer, primary_key=True)
name = Column(Text)
class Parcel(Base):
__tablename__ = 'parcel'
id = Column(Integer, primary_key=True)
sender_id = Column(Integer, ForeignKey('subject.id'))
receiver_id = Column(Integer, ForeignKey('subject.id'))
sender = relationship('Subject', foreign_keys=[sender_id], uselist=False, lazy='joined')
receiver = relationship('Subject', foreign_keys=[receiver_id], uselist=False, lazy='joined')
def __repr__(self):
return '<Parcel #{id} {s} -> {r}>'.format(id=self.id, s=self.sender.name, r=self.receiver.name)
# filling database
Base.metadata.create_all(engine)
p = Parcel()
p.sender, p.receiver = Subject(name='Bob'), Subject(name='Alice')
s.add(p)
s.flush()
#
# Method #1 - using `has` method - working but slow
print(s.query(Parcel).filter(Parcel.sender.has(name='Bob')).all())
、私はエラーが発生したエイリアスの関係で参加し、フィルタしてみました:
#
# Method #2 - using aliased joining - doesn't work
# I'm getting next error:
#
# sqlalchemy.exc.InvalidRequestError: Could not find a FROM clause to join from.
# Tried joining to <AliasedClass at 0x7f24b7adef98; Subject>, but got:
# Can't determine join between 'parcel' and '%(139795676758928 subject)s';
# tables have more than one foreign key constraint relationship between them.
# Please specify the 'onclause' of this join explicitly.
#
sender = aliased(Parcel.sender)
print(s.query(Parcel).join(sender).filter(sender.name == 'Bob').all())
私は、relatの代わりにjoin conditionを持つモデルを指定するとイオン、それは動作します。しかし、最終的なSQLクエリは、私が何を期待was'nt:
print(
s.query(Parcel)\
.join(Subject, Parcel.sender_id == Subject.id)\
.filter(Subject.name == 'Bob')
)
は、次のSQLクエリを生成します。
ここSELECT parcel.id AS parcel_id,
parcel.sender_id AS parcel_sender_id,
parcel.receiver_id AS parcel_receiver_id,
subject_1.id AS subject_1_id,
subject_1.name AS subject_1_name,
subject_2.id AS subject_2_id,
subject_2.name AS subject_2_name
FROM parcel
JOIN subject ON parcel.sender_id = subject.id
LEFT OUTER JOIN subject AS subject_1 ON subject_1.id = parcel.sender_id
LEFT OUTER JOIN subject AS subject_2 ON subject_2.id = parcel.receiver_id
WHERE subject.name = ?
あなたはsubject
テーブルには3回の代わりに2を接合されていることを見ることができます。これは、sender
とreceiver
のリレーションが両方ともロードされるように構成されているためです。そして、3回目の参加が私がフィルタリングしている件名です。
私は、最終的なクエリは次のようになりますことを期待:
SELECT parcel.id AS parcel_id,
parcel.sender_id AS parcel_sender_id,
parcel.receiver_id AS parcel_receiver_id,
subject_1.id AS subject_1_id,
subject_1.name AS subject_1_name,
subject_2.id AS subject_2_id,
subject_2.name AS subject_2_name
FROM parcel
LEFT OUTER JOIN subject AS subject_1 ON subject_1.id = parcel.sender_id
LEFT OUTER JOIN subject AS subject_2 ON subject_2.id = parcel.receiver_id
WHERE subject_1.name = ?
私は、複数の参照関係によるフィルタリングはそれほど不明瞭であってはならないし、それを行うには良いと明確な方法があると信じています。それを見つけるのを助けてください。
良い読み取り:http://docs.sqlalchemy.org/en/latest/orm/loading_relationships.html#結合されたeager-loadingのzen-of-zen-of-eager-loading。また、生成されたEXISTSサブクエリ式が本質的に遅いという主張は、少しDBに固有です。 Iirc Postgresqlは、例えば、そのためのセミジョインを生成することができます。 –