저는 몇 달 동안 SQL Alchemy를 사용 해왔고 지금까지 정말 감명 받았습니다.2 차 테이블 조인 동작과의 SQLAlchemy 관계가 지연로드와 열심히로드간에 변경되었습니다.
지금까지 버그가있는 것으로 보이는 문제가 하나 있지만 올바른 일을하고 있는지 확신 할 수 없습니다. 여기에 MS SQL을 사용하여 테이블 리플렉션을 사용하여 테이블 클래스를 정의하지만, 여기에 포함 된 코드 인 메모리 SQLite 데이터베이스를 사용하여 문제를 재현 할 수 있습니다.
내가하고있는 일은 두 테이블 간의 연결 테이블을 사용하여 여러 테이블간에 많은 관계를 정의하는 것입니다. 관계에 primaryjoin 문을 사용해야하는 링크 테이블에 링크를 필터링하는 데 사용할 정보가 하나 더 있습니다. 이것은 게으른 로딩에 완벽하게 작동하지만, 성능상의 이유로 우리는 열망하는 로딩과 모든 것을 넘어야하는 부분이 필요합니다.
나는 게으른 로딩과의 관계를 정의하는 경우 :
activefunds = relationship('Fund', secondary='fundbenchmarklink',
primaryjoin='and_(FundBenchmarkLink.isactive==True,'
'Benchmark.id==FundBenchmarkLink.benchmarkid,'
'Fund.id==FundBenchmarkLink.fundid)')
을 정상적으로 DB를 조회 :
query = session.query(Benchmark)
을 내가 원하는 동작 성능으로 인해, 정말 나쁜하지만, 내가 원하는 정확히입니다 모든 벤치 마크와 각자의 자금을 반복 할 때 여분의 SQL 쿼리에 적용됩니다.
나는 열망로드와의 관계 정의하는 경우 :
activefunds = relationship('Fund', secondary='fundbenchmarklink',
primaryjoin='and_(FundBenchmarkLink.isactive==True,'
'Benchmark.id==FundBenchmarkLink.benchmarkid,'
'Fund.id==FundBenchmarkLink.fundid)',
lazy='joined')
을 정상적으로 DB를 조회 :
query = session.query(Benchmark)
내 얼굴에 불어 :
sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) no such column: fund.id
[SQL: 'SELECT benchmark.id AS benchmark_id,
benchmark.name AS benchmark_name,
fund_1.id AS fund_1_id,
fund_1.name AS fund_1_name,
fund_2.id AS fund_2_id,
fund_2.name AS fund_2_name
FROM benchmark
LEFT OUTER JOIN (fundbenchmarklink AS fundbenchmarklink_1
JOIN fund AS fund_1 ON fund_1.id = fundbenchmarklink_1.fundid) ON benchmark.id = fundbenchmarklink_1.benchmarkid
LEFT OUTER JOIN (fundbenchmarklink AS fundbenchmarklink_2
JOIN fund AS fund_2 ON fund_2.id = fundbenchmarklink_2.fundid) ON fundbenchmarklink_2.isactive = 1
AND benchmark.id = fundbenchmarklink_2.benchmarkid
AND fund.id = fundbenchmarklink_2.fundid']
은 SQL 위 연결된 테이블이 열에 액세스하려고 시도하기 전에 조인되지 않은 것을 명확하게 보여줍니다.
나는 DB, 구체적으로 연결된 테이블에 가입 조회 할 경우 :
그것은 작동query = session.query(Benchmark).join(FundBenchmarkLink, Fund, isouter=True)
는, 그러나 그것이 내가 지금은 벤치 마크 테이블을 쿼리 할 때마다, 나는 항상 조인 정의 할 필요가 있는지 확인해야합니다 의미를 추가 테이블 두 개를 모두 추가하십시오.
내가 누락 된 부분이 있거나, 잠재적 인 버그입니까? 단순히 라이브러리의 작동 방식입니까? 난 당신이 primary join과 secondary join 비트를 혼합 한 생각
import logging
logging.basicConfig(level=logging.INFO)
logging.getLogger('sqlalchemy.engine.base').setLevel(logging.INFO)
from sqlalchemy import Column, DateTime, String, Integer, Boolean, ForeignKey, create_engine
from sqlalchemy.orm import relationship, sessionmaker
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class FundBenchmarkLink(Base):
__tablename__ = 'fundbenchmarklink'
fundid = Column(Integer, ForeignKey('fund.id'), primary_key=True, autoincrement=False)
benchmarkid = Column(Integer, ForeignKey('benchmark.id'), primary_key=True, autoincrement=False)
isactive = Column(Boolean, nullable=False, default=True)
fund = relationship('Fund')
benchmark = relationship('Benchmark')
def __repr__(self):
return "<FundBenchmarkLink(fundid='{}', benchmarkid='{}', isactive='{}')>".format(self.fundid, self.benchmarkid, self.isactive)
class Benchmark(Base):
__tablename__ = 'benchmark'
id = Column(Integer, primary_key=True)
name = Column(String, nullable=False)
funds = relationship('Fund', secondary='fundbenchmarklink', lazy='joined')
# activefunds has additional filtering on the secondary table, requiring a primaryjoin statement.
activefunds = relationship('Fund', secondary='fundbenchmarklink',
primaryjoin='and_(FundBenchmarkLink.isactive==True,'
'Benchmark.id==FundBenchmarkLink.benchmarkid,'
'Fund.id==FundBenchmarkLink.fundid)',
lazy='joined')
def __repr__(self):
return "<Benchmark(id='{}', name='{}')>".format(self.id, self.name)
class Fund(Base):
__tablename__ = 'fund'
id = Column(Integer, primary_key=True)
name = Column(String, nullable=False)
def __repr__(self):
return "<Fund(id='{}', name='{}')>".format(self.id, self.name)
if '__main__' == __name__:
engine = create_engine('sqlite://')
Base.metadata.create_all(engine)
maker = sessionmaker(bind=engine)
session = maker()
# Create some data
for bmkname in ['foo', 'bar', 'baz']:
bmk = Benchmark(name=bmkname)
session.add(bmk)
for fname in ['fund1', 'fund2', 'fund3']:
fnd = Fund(name=fname)
session.add(fnd)
session.add(FundBenchmarkLink(fundid=1, benchmarkid=1))
session.add(FundBenchmarkLink(fundid=2, benchmarkid=1))
session.add(FundBenchmarkLink(fundid=1, benchmarkid=2))
session.add(FundBenchmarkLink(fundid=2, benchmarkid=2, isactive=False))
session.commit()
# This code snippet works when activefunds doesn't exist, or doesn't use eager loading
# query = session.query(Benchmark)
# print(query)
# for bmk in query:
# print(bmk)
# for fund in bmk.funds:
# print('\t{}'.format(fund))
# This code snippet works for activefunds with eager loading
query = session.query(Benchmark).join(FundBenchmarkLink, Fund, isouter=True)
print(query)
for bmk in query:
print(bmk)
for fund in bmk.activefunds:
print('\t{}'.format(fund))
완벽한 [mcve]를 제공해 주셔서 감사합니다. –