Django ORM - 具有不同select子句的Grouped聚合

时间:2013-07-26 18:31:42

标签: mysql django django-models ranking django-orm

想象一下,我们有Django ORM模型Meetup,其定义如下:

class Meetup(models.Model):
    language = models.CharField()
    speaker = models.CharField()
    date = models.DateField(auto_now=True)

我想使用单个查询来获取语言,演讲者和日期 每种语言的最新活动。

>>> Meetup.objects.create(language='python', speaker='mike')
<Meetup: Meetup object>
>>> Meetup.objects.create(language='python', speaker='ryan')
<Meetup: Meetup object>
>>> Meetup.objects.create(language='node', speaker='noah')
<Meetup: Meetup object>
>>> Meetup.objects.create(language='node', speaker='shawn')
<Meetup: Meetup object>
>>> Meetup.objects.values("language").annotate(latest_date=models.Max("date")).values("language", "speaker", "latest_date")
[
    {'speaker': u'mike', 'language': u'python', 'latest_date': ...}, 
    {'speaker': u'ryan', 'language': u'python', 'latest_date': ...}, 
    {'speaker': u'noah', 'language': u'node', 'latest_date': ...}, 
    {'speaker': u'shawn', 'language': u'node', 'latest_date': ...}, 
]

D'哦!我们正在接受最新的活动,但错误的分组!

好像我需要一种GROUP BY languageSELECT的方法。 一组字段?


更新 - 这种查询似乎很容易在SQL中表达:

SELECT language, speaker, MAX(date)
FROM app_meetup
GROUP BY language;

我喜欢这样做而不使用Django的raw() - 是否有可能?

更新2 - 经过大量搜索,似乎在SO上有类似的问题:

更新3 - 最后,在@ danihp的帮助下,你似乎做得最好 是两个查询。我使用了以下方法:

# Abuse the fact that the latest Meetup always has a higher PK to build
# a ValuesList of the latest Meetups grouped by "language".
latest_meetup_pks = (Meetup.objects.values("language")
                                   .annotate(latest_pk=Max("pk"))
                                   .values_list("latest_pk", flat=True))

# Use a second query to grab those latest Meetups!
Meetup.objects.filter(pk__in=latest_meetup_pks)

这个问题是我上一个问题的后续问题:

Django ORM - Get latest record for group

1 个答案:

答案 0 :(得分:1)

这种查询易于解释但难以编写。如果这是SQL,我将建议您按照日期(desc)排序的语言按行排序超过分区的CTE过滤查询

但这不是SQL,这是django查询api。简单的方法是对每种语言进行查询:

languages = Meetup.objects.values("language", flat = True).distinct.order_by()
last_by_language = [  Meetup
                     .objects
                     .filter( language = l )
                     .latest( 'date' )
                     for l in languages
                    ]

如果某种语言没有会议,则会崩溃。 另一种方法是获取每种语言的所有最大数据:

last_dates = ( Meetup
             .objects
             .values("language")
             .annotate(ldate=models.Max("date"))
             .order_by() )

q= reduce(lambda q,meetup: 
     q | ( Q( language = meetup["language"] ) & Q( date = meetup["ldate"] ) ), 
     last_dates, Q())  

your_query = Meetup.objects.filter(q)

也许有人可以在没有原始sql的单个查询中解释如何执行此操作。

已编辑到期OP评论

您正在寻找:

"SELECT language, speaker, MAX(date) FROM app_meetup GROUP BY language"

并非所有rdbms都支持此表达式,因为未包含在select子句上的聚合函数中的所有字段都应出现在group by子句中。在您的情况下,speaker在select子句上(没有聚合函数),但不会出现在group by中。

在mysql中,他们不保证显示结果speaker最大日期相匹配。因此,我们不会面临简单的查询。

引用MySQL docs

  

在标准SQL中,包含GROUP BY子句的查询无法引用   选择列表中未分配的非聚合列   GROUP BY子句...... 但是,这主要适用于所有值   在GROUP BY中未命名的每个非聚合列中都是相同的   对于每个小组。

符合您要求的最接近的查询是:

Reults = (   Meetup
             .objects
             .values("language","speaker")
             .annotate(ldate=models.Max("date"))
             .order_by() )