SQLAlchemy ORM 从子查询选择多个实体
我需要查询多个实体,类似于 session.query(Entity1, Entity2)
,不过是从一个子查询中获取,而不是直接从表里获取。文档里有提到关于 从子查询中选择一个实体 的内容,但我找不到如何选择多个实体,无论是在文档里还是通过实验。
我的使用场景是,我需要通过一个窗口函数来过滤映射类背后的表,而在PostgreSQL中,这只能在子查询或公用表表达式(CTE)中完成。
补充说明:这个子查询涉及到两个表的连接,所以我不能仅仅使用 aliased(Entity1, subquery)
。
2 个回答
4
我在尝试做一些事情,跟原问题类似:用外连接把一个过滤过的表和另一个过滤过的表连接起来。我遇到了一些困难,因为这并不是很明显怎么做:
- 如何创建一个SQLAlchemy查询,让它能从两个表中返回数据。@zzzeek的回答教会了我怎么做:
get_session().query(A, B)
。 - 如何在这样的查询中把一个查询当作表来用。@zzzeek的回答也告诉了我这个方法:
filtered_a = aliased(A).filter(...).subquery()
。 - 如何在两个实体之间使用外连接。使用
select_from()
在outerjoin()
之后会破坏表之间的连接条件,导致出现笛卡尔积。根据@zzzeek的回答,我猜如果a
是aliased(),那么你可以在query()中包含a
,同时也用.outerjoin(a),这样就不会再连接一次,结果看起来是有效的。
直接按照@zzzeek建议的方法做,结果却出现了笛卡尔积(组合爆炸),因为我的一个模型使用了继承,而SQLAlchemy在内层SELECT之外添加了父表,没有任何条件!我觉得这可能是SQLAlchemy的一个bug。最后我采用的方法是:
filtered_a = aliased(A, A.query().filter(...)).subquery("filtered_a")
filtered_b = aliased(B, B.query().filter(...)).subquery("filtered_b")
query = get_session().query(filtered_a, filtered_b)
query = query.outerjoin(filtered_b, filtered_a.relation_to_b)
query = query.order_by(filtered_a.some_column)
for a, b in query:
...
20
在编程中,有时候我们会遇到一些问题,尤其是在使用特定的工具或库时。比如,有人可能会在使用某个库的过程中,发现它的某些功能不太好用,或者在文档中找不到想要的信息。这种情况下,大家通常会去网上查找解决方案,比如在StackOverflow这样的论坛上提问。
在提问时,最好能清楚地描述你遇到的问题,包括你使用的工具版本、你尝试过的解决办法,以及你希望实现的目标。这样其他人才能更好地理解你的问题,并给出有效的建议。
同时,提供一些代码示例也是很重要的,这样别人可以更直观地看到你的代码是怎么写的,问题出在哪里。记得在代码块中放上你的代码,这样更容易阅读。
总之,提问时要尽量详细和清晰,这样才能提高得到帮助的机会。
from sqlalchemy import *
from sqlalchemy.orm import *
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class A(Base):
__tablename__ = "a"
id = Column(Integer, primary_key=True)
bs = relationship("B")
class B(Base):
__tablename__ = "b"
id = Column(Integer, primary_key=True)
a_id = Column(Integer, ForeignKey('a.id'))
e = create_engine("sqlite://", echo=True)
Base.metadata.create_all(e)
s = Session(e)
s.add_all([A(bs=[B(), B()]), A(bs=[B()])])
s.commit()
# with_labels() here is to disambiguate A.id and B.id.
# without it, you'd see a warning
# "Column 'id' on table being replaced by another column with the same key."
subq = s.query(A, B).join(A.bs).with_labels().subquery()
# method 1 - select_from()
print s.query(A, B).select_from(subq).all()
# method 2 - alias them both. "subq" renders
# once because FROM objects render based on object
# identity.
a_alias = aliased(A, subq)
b_alias = aliased(B, subq)
print s.query(a_alias, b_alias).all()