简体   繁体   English

SQLAlchemy过滤相关对象的查询

[英]SQLAlchemy filter query by related object

Using SQLAlchemy , I have a one to many relation with two tables - users and scores. 使用SQLAlchemy ,我与两个表有一对多的关系 - 用户和分数。 I am trying to query the top 10 users sorted by their aggregate score over the past X amount of days. 我试图查询排名前10位的用户,这些用户按过去X天的总分进行排序。

users:  
  id  
  user_name  
  score  

scores:  
  user   
  score_amount  
  created  

My current query is: 我目前的查询是:

 top_users = DBSession.query(User).options(eagerload('scores')).filter_by(User.scores.created > somedate).order_by(func.sum(User.scores).desc()).all()  

I know this is clearly not correct, it's just my best guess. 我知道这显然不正确,这只是我最好的猜测。 However, after looking at the documentation and googling I cannot find an answer. 但是,在查看文档和谷歌搜索后,我找不到答案。

EDIT: Perhaps it would help if I sketched what the MySQL query would look like: 编辑:如果我勾勒出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

The single-joined-row way, with a group_by added in for all user columns although MySQL will let you group on just the "id" column if you choose: 单连接行方式,为所有用户列添加了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")

Or if you just want the users in the result: 或者,如果您只想在结果中使用用户:

sess.query(User).\
           join(User.scores).\
           filter(Score.created_at > someday).\
           group_by(User).\
           order_by(func.sum(Score.amount))

The above two have an inefficiency in that you're grouping on all columns of "user" (or you're using MySQL's "group on only a few columns" thing, which is MySQL only). 上面两个的效率很低,因为你在“用户”的所有列上进行分组(或者你只使用MySQL的“几列上的组”,这只是MySQL)。 To minimize that, the subquery approach: 为了最小化,子查询方法:

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)

An example of the identical scenario is in the ORM tutorial at: http://docs.sqlalchemy.org/en/latest/orm/tutorial.html#selecting-entities-from-subqueries 相同场景的一个示例位于ORM教程中: http//docs.sqlalchemy.org/en/latest/orm/tutorial.html#selecting-entities-from-subqueries

You will need to use a subquery in order to compute the aggregate score for each user. 您需要使用子查询来计算每个用户的总分。 Subqueries are described here: http://www.sqlalchemy.org/docs/05/ormtutorial.html?highlight=subquery#using-subqueries 子查询在此处描述: http ://www.sqlalchemy.org/docs/05/ormtutorial.html?highlight = subquery #using -subqueries

I am assuming the column (not the relation) you're using for the join is called Score.user_id, so change it if this is not the case. 我假设您用于连接的列(不是关系)称为Score.user_id,因此如果不是这样,请更改它。

You will need to do something like this: 你需要做这样的事情:

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]

However this will result in tuples of (user_id, total_score). 但是,这将导致(user_id,total_score)的元组。 I'm not sure if the computed score is actually important to you, but if it is, you will probably want to do something like this: 我不确定计算得分对你来说是否真的很重要,但如果是,你可能会想做这样的事情:

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))

This will result in 11 queries being executed, however. 但是,这将导致执行11个查询。 It is possible to do it all in a single query, but due to various limitations in SQLAlchemy, it will likely create a very ugly multi-join query or subquery (dependent on engine) and it won't be very performant. 可以在单个查询中完成所有操作,但由于SQLAlchemy中的各种限制,它可能会创建一个非常难看的多连接查询或子查询(依赖于引擎),并且它不会非常高效。

If you plan on doing something like this often and you have a large amount of scores, consider denormalizing the current score onto the user table. 如果您计划经常做这样的事情并且您有大量的分数,请考虑将当前分数非规范化到用户表中。 It's more work to upkeep, but will result in a single non-join query like: 维护更多的工作,但会导致一个非连接查询,如:

DBSession.query(User).order_by(User.computed_score.desc())

Hope that helps. 希望有所帮助。

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

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