Django ORM - 具有不同选择子句的分组聚合

Posted

技术标签:

【中文标题】Django ORM - 具有不同选择子句的分组聚合【英文标题】:Django ORM - Grouped aggregates with different select clauses 【发布时间】:2013-07-27 03:34:14 【问题描述】:

假设我们有 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': ..., 
]

天啊!我们收到了最新的活动,但分组有误!

似乎我需要一种方法来 GROUP BYlanguageSELECT 在不同的 一组字段?


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

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

我想要一种不使用 Django 的 raw() 的方法来做到这一点 - 有可能吗?

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

Django Query that gets the most recent objects How can I do a greatest n per group query in Django mysql calls this sort of query a group-wise maximum of a certain column。

更新 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

【问题讨论】:

很遗憾,这是 MySQL。在postgres中可以直接使用DISTINCT ON获取最新的group shameless plug for my answer on another question。 【参考方案1】:

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

但这不是 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 中,它们不是保证,而是显示结果 speakerma​​x date 匹配。因此,我们面临的查询并不容易。

引用MySQL docs:

在标准 SQL 中,包含 GROUP BY 子句的查询不能引用 到选择列表中未在 GROUP BY 子句...但是,这主要在所有值时很有用 在 GROUP BY 中未命名的每个非聚合列中是相同的 每个组。

最符合您要求的查询是:

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

【讨论】:

对查询集使用列表理解将为每种语言生成一个数据库匹配项。我认为您的第二个示例是不使用原始 sql 的最佳方法,但您需要使用 &amp; 而不是 ^ 加入 Q 对象。 @knbk,感谢您的 cmets。另外,感谢修复 and 错误。我是从脑海中写出来的……未经测试。此外,对于少数语言(3 或 4),第一种方法也是有效的,您同意吗? 是的,第一种方法也适用于少数语言,但即使只有两种不同的语言,您也会产生比第二种方法更多的查询。 我很欣赏这个答案,但我希望使用单个查询来做到这一点!它似乎?用 SQL 表达查询非常容易:SELECT language, speaker, MAX(date) FROM app_meetup GROUP BY language;我觉得应该有一些方法可以在 Django 中实现,而无需诉诸 .raw() :| @danihp 太糟糕了:) 我将用我找到的其他信息来扩展我的问题 - 在这一点上,我认为答案是“取决于你的数据库,你可能不能。”

以上是关于Django ORM - 具有不同选择子句的分组聚合的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Django 进行分组和聚合

Django ORM,group_by 按所有值分组

如何使用聚类对具有相似意图的句子进行分组?

Django 使用 ORM 和条件 Where 子句连接表

如何在sqlite django ORM中实现have子句

如何在 django ORM 的 From 子句中编写子查询