4

想象一下,我们有一个Meetup具有以下定义的 Django ORM 模型:

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但在一languageSELECT不同的字段上?


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

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

我很想在不使用 Django 的情况下做到这一点raw()- 有可能吗?

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

更新 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 - 获取组的最新记录

4

1 回答 1

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 中,它们不是保证,而是显示的结果speakermax date匹配。因此,我们面临的不是一个简单的查询。

引用MySQL 文档

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

最符合您要求的查询是:

Reults = (   Meetup
             .objects
             .values("language","speaker")
             .annotate(ldate=models.Max("date"))
             .order_by() )
于 2013-07-26T19:10:16.837 回答