简体   繁体   English

SQLAlchemy ORM:产品总和

[英]SQLAlchemy ORM: Sum of products

Let's say I have a ROOMS table with width and length columns, and a corresponding SQLAlchemy model. 假设我有一个带有width和length列的ROOMS表,以及一个对应的SQLAlchemy模型。 Is there a clean and efficient way to get a total area of all rooms, ie sum(length x width)? 是否有一种干净有效的方法来获取所有房间的总面积,即sum(长x宽)? It's easy enough to do by looping at the client but it must surely be more efficient if it can be fetched by a query at the server. 通过在客户端循环很容易做到,但是如果可以通过服务器上的查询来获取它,则肯定必须更加高效。

Edit: 编辑:

I thought I was being helpful by reducing the problem to a simple, clean example but I now realise I was just shooting myself in the foot because my difficulties evidently stem from a more fundamental lack of understanding of SQLAlchemy and working with ORMs. 我以为我可以通过将问题简化为一个简单的示例而有所帮助,但是现在我意识到我只是在开枪,因为我的困难显然是由于对SQLAlchemy的更根本的了解以及与ORM的合作。

My model (flask-sqlalchemy) actually involves three related tables: holdings, commodities and prices. 我的模型(flask-sqlalchemy)实际上包含三个相关的表:持有量,商品和价格。 Commodities have many prices, and each holding is a quantity of a given commodity. 大宗商品有很多价格,每个持有量都是给定商品的数量。 I have it set up as follows: 我将其设置如下:

class Holding(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    time = db.Column(db.TIMESTAMP, index=True)
    quantity = db.Column(db.DECIMAL(10,5))
    commodity_id = db.Column(db.Integer, db.ForeignKey('commodity.id'))

    commodity = db.relationship('Commodity', back_populates='holdings')

class Commodity(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    symbol = db.Column(db.String(20))
    name = db.Column(db.String(150))

    holdings = db.relationship('Holding', back_populates='commodity', lazy='dynamic')
    prices = db.relationship('Price', back_populates='commodity', lazy='dynamic', order_by='Price.time.desc()')

class Price(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    time = db.Column(db.TIMESTAMP, index=True)
    amount = db.Column(db.DECIMAL(10,5), index=True)
    commodity_id = db.Column(db.Integer, db.ForeignKey('commodity.id'))

    commodity = db.relationship('Commodity', back_populates='prices')

I want the sum of Holding.quantity * Holding.commodity.[most recent price]. 我想要Holding.quantity * Holding.commodity。[最新价格]的总和。

Since Commodity.prices is in descending time order I can easily calculate the value in a Holding loop examining: 由于Commodity.prices是降序排列的,因此我可以在Holding循环中轻松地计算值:

h.commodity.prices.first().amount * h.quantity

... but I can't see how to get at the related Price details from a single query, so I don't know how to apply @leovp's solution. ...但是我看不到如何从单个查询中获得相关的价格详细信息,所以我不知道如何应用@leovp的解决方案。

I hope that properly describes the problem now, apologies for the false start. 我希望现在能正确地描述问题,对错误的开始表示歉意。

The more interesting part about your question is solving the problem. 关于您的问题更有趣的部分是解决问题。 Now, I'm pretty green when it comes to MySQL, so there might be more efficient solutions to that than this: 现在,我对MySQL相当了解,因此可能有比这更有效的解决方案:

In [43]: price_alias = db.aliased(Price)

In [44]: latest_price = db.session.query(Price.commodity_id, Price.amount).\
    ...:     outerjoin(price_alias,
    ...:               db.and_(price_alias.commodity_id == Price.commodity_id,
    ...:                       price_alias.time > Price.time)).\
    ...:     filter(price_alias.id == None).\
    ...:     subquery()

The self left join tries to join rows with greater time , which do not exist for the latest price, hence the filter(price_alias.id == None) . 自左filter(price_alias.id == None)尝试以更长的时间 filter(price_alias.id == None)行,这些行对于最新价格不存在,因此使用filter(price_alias.id == None)

What's left then is to just join Holding s with the subquery: 然后剩下的就是将Holding s与子查询一起加入:

In [46]: sum_of_products = db.session.query(
    ...:         db.func.sum(Holding.quantity * latest_price.c.amount)).\
    ...:     join(latest_price,
    ...:          latest_price.c.commodity_id == Holding.commodity_id).\
    ...:     scalar()

Assuming your model is named Room and it has properties such as length and width : 假设您的模型名为Room,并且具有诸如lengthwidth属性:

from sqlalchemy import func
total_area = session.query(func.sum(Room.length * Room.width))

Which is going to be translated as something like that: 它将被翻译成这样的东西:

SELECT sum(rooms.length * rooms.width) AS sum_1 
FROM rooms

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM