SQLAlchemy:一个查询中有几个计数

kre*_*reo 10 python sql orm sqlalchemy querying

我很难优化我的SQLAlchemy查询.我的SQL知识非常基础,我无法从SQLAlchemy文档中获取所需的东西.

假设以下非常基本的一对多关系:

class Parent(Base):
    __tablename__ = "parents"
    id = Column(Integer, primary_key = True)
    children = relationship("Child", backref = "parent")

class Child(Base):
    __tablename__ = "children"
    id = Column(Integer, primary_key = True)
    parent_id = Column(Integer, ForeignKey("parents.id"))
    naughty = Column(Boolean)
Run Code Online (Sandbox Code Playgroud)

我怎么能:

  • 查询(Parent, count_of_naughty_children, count_of_all_children)每个父母的元组?

花了大量时间谷歌搜索后,我发现如何分别查询这些值:

# The following returns tuples of (Parent, count_of_all_children):
session.query(Parent, func.count(Child.id)).outerjoin(Child, Parent.children).\
    group_by(Parent.id)
# The following returns tuples of (Parent, count_of_naughty_children):
al = aliased(Children, session.query(Children).filter_by(naughty = True).\
    subquery())
session.query(Parent, func.count(al.id)).outerjoin(al, Parent.children).\
    group_by(Parent.id)
Run Code Online (Sandbox Code Playgroud)

我尝试以不同的方式将它们组合在一起,但却无法得到我想要的东西.

  • 查询所有顽皮孩子超过80%的父母?编辑:顽皮可能是NULL.

我猜这个查询将基于前一个查询,按淘气/全部比率过滤.

任何帮助表示赞赏.

编辑:感谢Antti Haapala的帮助,我找到了第二个问题的解决方案:

avg = func.avg(func.coalesce(Child.naughty, 0)) # coalesce() treats NULLs as 0
# avg = func.avg(Child.naughty) - if you want to ignore NULLs
session.query(Parent).join(Child, Parent.children).group_by(Parent).\
    having(avg > 0.8)
Run Code Online (Sandbox Code Playgroud)

它找到了孩子的naughty变量的平均值,将False和NULL视为0,将True视为1.使用MySQL后端测试,但也应该在其他人上工作.

Sin*_*ion 9

count()SQL aggretate功能是相当简单的; 它为您提供每组中非空值的总数.考虑到这一点,我们可以调整您的查询以给您正确的结果.

print (Query([
    Parent,
    func.count(Child.id),
    func.count(case(
        [((Child.naughty == True), Child.id)], else_=literal_column("NULL"))).label("naughty")])

    .join(Parent.children).group_by(Parent)
    )
Run Code Online (Sandbox Code Playgroud)

哪个产生以下sql:

SELECT 
 parents.id AS parents_id, 
 count(children.id) AS count_1, 
 count(CASE WHEN (children.naughty = 1) 
       THEN children.id 
       ELSE NULL END) AS naughty 
FROM parents 
JOIN children ON parents.id = children.parent_id 
GROUP BY parents.id
Run Code Online (Sandbox Code Playgroud)


Ant*_*ala 5

如果你的查询只是为了让那些有80%以上孩子的父母顽皮,你可以在大多数数据库中将其转换naughty为整数,然后取其平均值; 然后having这个平均值大于0.8.

因此你得到类似的东西

from sqlalchemy.sql.expression import cast

naughtyp = func.avg(cast(Child.naughty, Integer))
session.query(Parent, func.count(Child.id), naughtyp).join(Child)\
    .group_by(Parent.id).having(naughtyp > 0.8).all()
Run Code Online (Sandbox Code Playgroud)

  • func.coalesce()帮助我修复了这个^^ (2认同)