2010-01-06 135 views
12

使用SQLAlchemy,我与两个表 - 用户和分数有一对多的关系。我正在尝试查询在过去的X天内按总分排序的前10名用户。SQLAlchemy过滤查询相关对象

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

我知道,这显然是不正确的,这只是我最好的猜测。但是,在查看文档和使用Google搜索后,我找不到答案。

编辑: 也许这将有助于如果我勾勒出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

误差在使用结合eagerload()用针对则其加入在此说明FAQ条目:http://www.sqlalchemy。org/trac/wiki/FAQ#ImusinglazyFalsetocreateaJOINOUTERJOINandSQLAlchemyisItrustctingtheWhenItemtoItemtoIndtoWAWEEORDERBYLIMITetc.whichresliesToOUTERJOIN – zzzeek 2010-01-06 19:58:29

回答

14

单连排的方式,以增加在所有用户列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

+0

嗨,感谢您的回复。这工作得很好,文档帮助了很多。我如何获得用户的score_increase访问权限?举例来说,查询分配给变量top_users,并循环遍历每个用户。 user.score_increase不起作用,user.UserScore.score_increase也不起作用。 – Marc 2010-01-06 22:30:56

+0

使用第三个查询,如果您通过sess.query(User,subq.c.score_increase)迭代,您将得到(User,score_increase)的元组 – zzzeek 2010-01-07 01:36:06

+0

嗯我必须在这里丢失一些东西。万一它很重要,我使用Turbogears 2,我将第三个查询的结果分配给我的模板中可用的变量top_users。然后我循环 - 对于top_users中的用户:print user.user_name +''+ user.score_increase - 基本上我想显示用户得分的数量增加了过去的x天数。 我不明白如何访问top_users元组内的连接数据。 – Marc 2010-01-07 05:42:40

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

希望有所帮助。

+1

yikes。没有我知道的这种限制。 – zzzeek 2010-01-06 19:50:50