[英]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 greatest-n-per-group problem. 关于您的问题更有趣的部分是解决每组最多n个问题。 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,并且具有诸如
length
和width
属性:
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.