[英]SQLAlchemy - subquery in a WHERE clause
我最近才开始使用 SQLAlchemy 并且仍然无法理解一些概念。
归结为基本元素,我有两个这样的表(这是通过 Flask-SQLAlchemy):
class User(db.Model):
__tablename__ = 'users'
user_id = db.Column(db.Integer, primary_key=True)
class Posts(db.Model):
__tablename__ = 'posts'
post_id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey('users.user_id'))
post_time = db.Column(db.DateTime)
user = db.relationship('User', backref='posts')
我将如何查询用户列表及其最新帖子(不包括没有帖子的用户)。 如果我使用的是 SQL,我会这样做:
SELECT [whatever]
FROM posts AS p
LEFT JOIN users AS u ON u.user_id = p.user_id
WHERE p.post_time = (SELECT MAX(post_time) FROM posts WHERE user_id = u.user_id)
所以我确切地知道“想要的”SQL 以获得我想要的效果,但不知道如何在 SQLAlchemy 中“正确”表达它。
编辑:如果它很重要,我在 SQLAlchemy 0.6.6 上。
这应该有效(不同的 SQL,结果相同):
t = Session.query(
Posts.user_id,
func.max(Posts.post_time).label('max_post_time'),
).group_by(Posts.user_id).subquery('t')
query = Session.query(User, Posts).filter(and_(
User.user_id == Posts.user_id,
User.user_id == t.c.user_id,
Posts.post_time == t.c.max_post_time,
))
for user, post in query:
print user.user_id, post.post_id
其中 c 代表“列”
先前的答案有效,但您要求的确切 sql 也与实际陈述非常相似:
print s.query(User, Posts).\
outerjoin(Posts.user).\
filter(Posts.post_time==\
s.query(
func.max(Posts.post_time)
).
filter(Posts.user_id==User.user_id).
correlate(User).
as_scalar()
)
我想不一定明显的“概念”是当前需要 as_scalar() 来将子查询建立为“标量”(它可能应该从上下文中假设 ==)。
编辑:已确认,这是错误行为,已完成票证 #2190。 在当前的提示或版本 0.7.2 中,自动调用 as_scalar() 并且上面的查询可以是:
print s.query(User, Posts).\
outerjoin(Posts.user).\
filter(Posts.post_time==\
s.query(
func.max(Posts.post_time)
).
filter(Posts.user_id==User.user_id).
correlate(User)
)
它的表达通常与实际的 SQL 类似 - 您创建一个返回单个结果并与之进行比较的子查询 - 但是,如果您必须在子查询中使用您已经在查询或加入的表,有时可能会很痛苦。
解决方案是创建 model 的别名版本以在子查询中引用。
因此,假设您已经在一个连接中操作,其中您有一个现有的Posts
model
和一些基本query
准备就绪 - 现在,您想要查询每个用户的最新(单个)帖子列表,您将过滤查询喜欢:
from sqlalchemy.orm import aliased
posts2 = aliased(Posts) # create aliased version
query = query.filter(
model.post_id
==
Posts.query # create query directly from model, NOT from the aliased version!
.with_entities(posts2.post_id) # only select column "post_id"
.filter(
posts2.user_id == model.user_id
)
.order_by(posts2.post_id.desc()) # assume higher id == newer post
.limit(1) # we must limit to a single row so we only get 1 value
)
我故意不使用func.max
因为我认为这是一个更简单的版本,并且它已经在其他答案中,我认为这个示例对通常发现这个问题的人很有用,因为他们正在寻找如何子查询的解决方案同一张桌子。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.