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 BY
和 language
但 SELECT
在不同的
一组字段?
更新 - 这种查询似乎很容易用 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 中,它们不是保证,而是显示结果 speaker
与 max date 匹配。因此,我们面临的查询并不容易。
引用MySQL docs:
在标准 SQL 中,包含 GROUP BY 子句的查询不能引用 到选择列表中未在 GROUP BY 子句...但是,这主要在所有值时很有用 在 GROUP BY 中未命名的每个非聚合列中是相同的 每个组。
最符合您要求的查询是:
Reults = ( Meetup
.objects
.values("language","speaker")
.annotate(ldate=models.Max("date"))
.order_by() )
【讨论】:
对查询集使用列表理解将为每种语言生成一个数据库匹配项。我认为您的第二个示例是不使用原始 sql 的最佳方法,但您需要使用&
而不是 ^
加入 Q
对象。
@knbk,感谢您的 cmets。另外,感谢修复 and
错误。我是从脑海中写出来的……未经测试。此外,对于少数语言(3 或 4),第一种方法也是有效的,您同意吗?
是的,第一种方法也适用于少数语言,但即使只有两种不同的语言,您也会产生比第二种方法更多的查询。
我很欣赏这个答案,但我希望使用单个查询来做到这一点!它似乎?用 SQL 表达查询非常容易:SELECT language, speaker, MAX(date) FROM app_meetup GROUP BY language;我觉得应该有一些方法可以在 Django 中实现,而无需诉诸 .raw()
:|
@danihp 太糟糕了:) 我将用我找到的其他信息来扩展我的问题 - 在这一点上,我认为答案是“取决于你的数据库,你可能不能。”
以上是关于Django ORM - 具有不同选择子句的分组聚合的主要内容,如果未能解决你的问题,请参考以下文章