SQLAlchemy:测试关系是否存在而不实际加载它

时间:2015-04-21 12:39:19

标签: python performance sqlalchemy

我不确定这是否可行,但我在SQLite中使用SQLAlchemy有一个层次结构类型结构。 在我的层次结构中,我想向用户表明父母有孩子,而不需要加载所有孩子。 我知道SQLAlchemy使用延迟加载,但是当我访问关系属性时,整个列表都被加载了。由于父母可以拥有数千名孩子,因此仅仅测试children != None就会产生相当大的性能开销。

目前,该关系定义如下:

children = relationship('child',
                        cascade='all',
                        backref=backref('parent'),
                        )

我目前正在测试儿童:

qry = session.query(parenttable).all()

for parent in qry:
    if parent.children != None:
        childrenindication = [{'Name': '...'}]
    else:
        childrenindication = []

    hierarchylist.append({
                'Name': parent.name,
                'Children': childrenindication
                })

如果有更友好的表现方式,那将会很棒。

1 个答案:

答案 0 :(得分:4)

假设样本模型:

class Parent(Base):
    __tablename__ = 'parent'

    id = Column(Integer, primary_key=True)
    name = Column(String, nullable=False)

    children = relationship("Child", cascade="all", backref="parent")


class Child(Base):
    __tablename__ = 'child'

    id = Column(Integer, primary_key=True)
    name = Column(String, nullable=False)
    parent_id = Column(ForeignKey(Parent.id))

下面列出了几个选项,其中第一个选项是您问题的最直接答案:

选项-1:使用Relationship.any(...) - 可能是最快的

has_children = Parent.children.any()
q = session.query(Parent, has_children)
for parent, has_children in q.all():
    print(parent, has_children)

选项-2:获取使用子查询的子项数

# @note: returns None instead of 0 for parent with no children
from sqlalchemy import func
subq = (
    session.query(Child.parent_id, func.count(Child.id).label("num_children"))
    .group_by(Child.parent_id)
    .subquery()
)
q = (session
     .query(Parent, subq.c.num_children)
     .outerjoin(subq, Parent.id == subq.c.parent_id)
     )
for parent, has_children in q.all():
    print(parent, has_children)

选项-3:获取没有子查询的子节点数(如果父表是

,则很好)
# not have many columns
from sqlalchemy import func
q = (session
     .query(Parent, func.count(Child.id).label("num_children"))
     .outerjoin(Child, Parent.children)
     .group_by(Parent)
     )
for parent, has_children in q.all():
    print(parent, has_children)