我可以在Django 1.3的ORM中控制GROUP BY吗?

5 投票
2 回答
1864 浏览
提问于 2025-04-16 22:18

我觉得用一个例子来解释会更好。

下面是数据的样子:

|project            |
|id|name            |
|1 |some project    |
|2 |my other project|

|run                                  |
|id|project_id|start_time   |result   |
|1 |1         |1305732581845|something|
|2 |1         |1305732593721|nothing  |
|3 |2         |1305732343721|nothing  |
|4 |2         |1305732556821|something|

我想从每个项目的最新运行中获取整个记录集。SQL查询大概是这样的:

SELECT *, MAX("run"."start_time")
FROM "run"    
LEFT OUTER JOIN "project" ON ("run"."project_id" = "project"."id") 
GROUP BY "project"."id"

这个查询会返回我两个表中最新项目运行的所有列,太好了,这正是我需要的。

但是在尝试找到django 1.3中对应的ORM方法时,我就是找不到合适的方式。如果我这样做:

Run.objects.annotate(Max('start_time'))

生成的SQL查询大概是这样的:

SELECT 
"run"."id", "run"."result", "run"."project_id", "project"."id", "project"."name", 
MAX("run"."start_time")
FROM "run"
LEFT OUTER JOIN "project" ON ("run"."project_id" = "project"."id")
GROUP BY "run"."id", "run"."result", "run"."project_id", "project"."id", "project"."name"

这样并不能给我正确的结果,因为分组的方式不符合我的需求。我记得在django的早期版本中,下面的写法可以正确地设置查询中的分组条件,但在1.3版本中似乎不管用:

q = Run.objects.annotate(Max('start_time'))
q.query.group_by = [("project", "id")]

在1.3中,这样生成的查询和不手动修改分组属性的查询是完全一样的。

我还尝试了根据文档中对.values()在调用.annotate()前后的行为的描述来进行逻辑上的处理,但结果并没有如预期那样工作。当我尝试这样做时:

q = Run.objects.values('project__id').annotate(Max('start_time')).values('id')

我得到的查询是这样的:

SELECT 
"run"."id", "run"."project_id"
MAX("run"."start_time")
FROM "run"
LEFT OUTER JOIN "project" ON ("run"."project_id" = "project"."id")
GROUP BY "run"."id", "run"."project_id"

有没有人能告诉我正确的方法来实现我想做的事情,而不需要以下这些:

  • 使用原始SQL - 如果我总是得自己写查询,那用ORM有什么意义呢?
  • 使用.extra(select = {'latest': 'somequery'}) - 为什么我需要使用子查询,而一个没有子查询的有效查询就能满足我的需求呢?
  • 使用多个查询来获取相同的数据 - 再说一次,为什么我需要进行多个查询才能得到一个查询就能得到的结果呢?

2 个回答

0

这部分在文档的注释部分讲得很简单明了,之前的版本中你是无法手动设置分组的。

YourModel.objects.values('this_is_your_group_by', 'even_a_second_field').annotate(sum=Sum('your_field'))
1

简而言之:Django确实让你控制“分组”这个部分,但它的限制是要兼容所有类型的SQL,所以我无法实现我想要的效果。

有人提醒我,我想用Django的ORM生成的原始查询在所有类型的SQL中其实并不合法。这里是我想要的查询的回顾:

SELECT *, MAX("run"."start_time")
FROM "run"    
LEFT OUTER JOIN "project" ON ("run"."project_id" = "project"."id") 
GROUP BY "project"."id"

如果一个人试图在MSSQL中选择不在“分组”中的内容,实际上会出现错误。所以我觉得Django其实不应该让我生成这样的查询,我基本上是在错误地尝试解决我的问题。

撰写回答