使用SQLAlchemy,我与两个表有一对多的关系 - 用户和分数。我正在尝试查询前10位用户,这些用户按过去X天的总分进行排序。
users:
id
user_name
score
scores:
user
score_amount
created
我目前的查询是:
top_users = DBSession.query(User).options(eagerload('scores')).filter_by(User.scores.created > somedate).order_by(func.sum(User.scores).desc()).all()
我知道这显然不正确,这只是我最好的猜测。但是,在查看文档和谷歌搜索后,我找不到答案。
编辑: 如果我勾勒出MySQL查询的样子,也许会有所帮助:
SELECT user.*, SUM(scores.amount) as score_increase
FROM user LEFT JOIN scores ON scores.user_id = user.user_id
WITH scores.created_at > someday
ORDER BY score_increase DESC
答案 0 :(得分:18)
单联接行方式,为所有用户列添加了group_by
,但如果您选择,MySQL将允许您将“id”列分组:
sess.query(User, func.sum(Score.amount).label('score_increase')).\
join(User.scores).\
filter(Score.created_at > someday).\
group_by(User).\
order_by("score increase desc")
或者,如果您只想要结果中的用户:
sess.query(User).\
join(User.scores).\
filter(Score.created_at > someday).\
group_by(User).\
order_by(func.sum(Score.amount))
上面两个的效率很低,因为你在“用户”的所有列上进行分组(或者你只使用MySQL的“仅在几列上的组”的东西,这只是MySQL)。为了尽量减少这种情况,子查询方法:
subq = sess.query(Score.user_id, func.sum(Score.amount).label('score_increase')).\
filter(Score.created_at > someday).\
group_by(Score.user_id).subquery()
sess.query(User).join((subq, subq.c.user_id==User.user_id)).order_by(subq.c.score_increase)
相同场景的一个示例位于ORM教程中:http://docs.sqlalchemy.org/en/latest/orm/tutorial.html#selecting-entities-from-subqueries
答案 1 :(得分:1)
您需要使用子查询来计算每个用户的总分数。子查询在此处描述:http://www.sqlalchemy.org/docs/05/ormtutorial.html?highlight=subquery#using-subqueries
答案 2 :(得分:0)
我假设您用于连接的列(不是关系)称为Score.user_id,因此如果不是这样,请更改它。
你需要做这样的事情:
DBSession.query(Score.user_id, func.sum(Score.score_amount).label('total_score')).group_by(Score.user_id).filter(Score.created > somedate).order_by('total_score DESC')[:10]
然而,这将导致(user_id,total_score)的元组。我不确定计算出的分数对你来说是否真的很重要,但如果是,你可能会想做这样的事情:
users_scores = []
q = DBSession.query(Score.user_id, func.sum(Score.score_amount).label('total_score')).group_by(Score.user_id).filter(Score.created > somedate).order_by('total_score DESC')[:10]
for user_id, total_score in q:
user = DBSession.query(User)
users_scores.append((user, total_score))
然而,这将导致执行11个查询。可以在单个查询中完成所有操作,但由于SQLAlchemy中的各种限制,它可能会创建一个非常难看的多连接查询或子查询(依赖于引擎),并且它不会非常高效。
如果您计划经常这样做并且您有大量分数,请考虑将当前分数非规范化到用户表中。维护更多的工作,但会导致一个非连接查询,如:
DBSession.query(User).order_by(User.computed_score.desc())
希望有所帮助。