【问题标题】:How to join 3 tables and perform func.sum如何加入 3 个表并执行 func.sum
【发布时间】:2014-06-30 23:55:54
【问题描述】:

我如何加入 3 个表,客户、订单和存款,并为数据库中存在的每个 Client.id 对 Orders.total 和 Deposits.total 执行 func.sum ?查询结果应包括列 Clients.email、func.sum(Orders.total) 和 func.sum(Deposits.total)。

到目前为止,我已经尝试了不同的查询:

listeclients = db.session.query(Clients,func.sum(Clients.orders.total).\
    label("ctotal"),func.sum((Clients.deposits.total).\
    label("dtotal"))).group_by(Client.id).all()

给我不同的错误,例如:

AttributeError: Neither 'InstrumentedAttribute' object nor 'Comparator' object associated with Clients.orders has an attribute 'total'

我想看看在 sqlalchemy 中是如何做到这一点的,但我也愿意接受这种查询逻辑背后的提示......

我的映射是否正确?这种连接的语法是什么?我应该在某处使用 eagerload 吗?我已经通过更简单的查询取得了成功,但是现在这样的查询已经超出了我的想象!欢迎任何帮助,即使只是原始 SQL 中的逻辑。我被这个困住了......

class Clients(db.Model):
    __tablename__ = 'clients'    
    id = db.Column(db.Integer, primary_key = True)
    email = db.Column(db.String(60), index = True, unique = True)
    adresse = db.Column(db.String(64), index = True)
    telephone = db.Column(db.String(10), index = True)
    confirmed = db.Column(db.Boolean, default = False)
    orders = db.relationship('Orders')
    deposits = db.relationship('Deposits') 

class Orders(db.Model):
    __tablename__ = 'orders'    
    id = db.Column(db.Integer, primary_key = True)
    client_id = db.Column(db.Integer, db.ForeignKey('clients.id'))
    total = db.Column(db.Float)
    date = db.Column(db.DateTime, index = True, default=datetime.now)    
    client = db.relationship('Clients')

class Deposits(db.Model):
    __tablename__='deposits'
    id = db.Column(db.Integer, primary_key = True)
    date = db.Column(db.DateTime, index = True, default=datetime.now)    
    client_id = db.Column(db.Integer, db.ForeignKey('clients.id'))
    total = db.Column(db.Float)  
    cheque = db.Column(db.Boolean)
    client = db.relationship('Clients')

【问题讨论】:

    标签: python sql sqlite sqlalchemy


    【解决方案1】:

    更新:更新了下面的查询以正确处理sum

    sq1 = (db.session.query(Orders.client_id, func.sum(Orders.total).label("ctotal"))
            .group_by(Orders.client_id)).subquery("sub1")
    
    sq2 = (db.session.query(Deposits.client_id, func.sum(Deposits.total).label("dtotal"))
            .group_by(Deposits.client_id)).subquery("sub2")
    
    q = (db.session.query(Clients, sq1.c.ctotal, sq2.c.dtotal)
        .outerjoin(sq1, sq1.c.client_id == Clients.id)
        .outerjoin(sq2, sq2.c.client_id == Clients.id)
        )
    

    另外,您可以简单地使用backref

    class Clients(db.Model):
        orders = db.relationship('Orders', backref='client')
        deposits = db.relationship('Deposits', backref='client') 
    
    class Orders(db.Model):
        # client = db.relationship('Clients')
    
    class Deposits(db.Model):
        # client = db.relationship('Clients')
    

    【讨论】:

    • 太好了,这个查询就像一个魅力!荣誉和我刚刚在那里学到了很多关于查询和加入的知识……在我现在写更多查询的路上!美好的一天
    • 实际上,我只是在测试它在数据库中添加更多数据,并且在返回查询中发生了一些奇怪的事情:只要有一个以上 Order 或 Deposit ,函数的结果.sum() 乘以 2 ... 是否有某种我缺少的递归性?我应该在某处添加“过滤器”吗?我应该如何追踪这种不当行为?按 Client.id 查询和按 Client.id 分组不会改变这种行为?
    • 所以这个查询需要稍微调整一下,我还没有发现如何,但问题是:如果一个 client.id 有一个存款,一切都很好,如果他有两个存款,查询返回的值是应有值的两倍,对于 3 次存款,它是应有值的三倍,依此类推......我可能会在将 func.sum() 除以 func.count( ),但它并不漂亮!我错过了什么?为什么 Deposits 中的条目数是 func.sum() 中的一个因素?
    • 更新了解决这个问题的答案,其中原始答案是一个非常典型的错误。基本上需要为每个表单独聚合数据。然后查询返回整个 Clients 对象,如果您只需要电子邮件,您可能希望将其替换为 Clients.email
    【解决方案2】:

    在 sql 中很简单:

    select c.email, sum(o.total), sum(d.total)
    from Clients c
    left join Orders o
        on ...
    left join Deposits d
        on ...
    group by c.email
    

    【讨论】:

    • 感谢您的回答,这看起来非常合乎逻辑和简单,但我仍在尝试在 sqlalchemy 中解决它,但没有取得多大成功。我用 listeclients = db.session.query(Orders,func.sum(Orders.total).label('ctotal')).group_by(Orders.client_id).all() 实现了大约一半的需求。 . 但是一旦我尝试 .join(Depot,func.sum(Depot.montant).label('dtotal')) 我得到错误......所以我必须学会正确加入!回去工作!
    猜你喜欢
    • 1970-01-01
    • 2014-02-27
    • 1970-01-01
    • 2011-02-01
    • 2012-04-03
    • 2017-10-27
    • 2013-08-29
    • 2019-01-04
    • 2020-10-17
    相关资源
    最近更新 更多