2013-07-26 85 views
3

假設我們有如下定義Django的ORM模型MeetupDjango的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': ...}, 
] 

D'oh!我們正在收到最新的活動,但對於錯誤的分組!

看來我需要一種方式來GROUP BYlanguageSELECT在不同的 集字段?


更新 - 這種查詢似乎很容易在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 - Get latest record for group

+0

無賴,這是MySQL的。在postgres中,你可以直接使用DISTINCT ON來獲取組的最新版本[無恥的插件來解決我在另一個問題上的答案](http://stackoverflow.com/a/20129229/1309332)。 – dbn

回答

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子句中的所有字段應該出現在組按條款。在你的情況下,speaker是在select子句(沒有聚合函數),但不出現在group by中。

在mysql中,它們不是保證比顯示結果speaker是與匹配最大日期。因爲這樣,我們並不面臨簡單的查詢。

報價MySQL docs

在標準的SQL,包括GROUP BY子句不能在沒有在 GROUP BY子句中命名的選擇列表請參考 到非聚合列的查詢...... 然而,這對於每個組中未命名的每個非聚合列中的所有值 對於每個組都是相同的 都很有用。

最貼近查詢,以符合您的要求是:

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

對查詢集使用列表理解將爲每種語言生成數據庫匹配。我認爲你的第二個例子是不使用raw sql的最好方法,但是你需要用'&'而不是'^'來加入'Q'對象。 – knbk

+0

@knbk,謝謝你的評論。此外,感謝修復'和'錯誤。我從我的腦海裏寫下了......沒有經過測試。另外,對於少數語言(3或4),第一種方法也是有效的,您同意嗎? – danihp

+0

是的,第一種方法對少數語言也有效,但即使只有2種不同的語言,也會產生比第二種方法更多的查詢。 – knbk