Django:按最新的每个不同列过滤

时间:2014-05-30 17:13:20

标签: python sql django distinct aggregate

鉴于此FruitBasket模型,

class FruitBasket(Model):
    fruit = CharField(max_length=128)
    count = PositiveIntegerField()

这个样本数据,

id      fruit        count
-----   ----------   -----
0       apple        10
1       banana       20
2       apple        5
3       banana       30

我想要一个返回以下项目的django查询:

[(2,apple,5),(3,banana,30)]

基本上,抓住最新的" row per fruit(我在此示例中简化了rowid的时间戳。)

4 个答案:

答案 0 :(得分:6)

https://docs.djangoproject.com/en/dev/ref/models/querysets/#distinct

q = FruitBasket.objects.distinct('fruit')

仅在使用postgres时才有效。

  

仅在PostgreSQL上,您可以传递位置参数(*字段)   命令指定DISTINCT应该应该的字段的名称   应用。这转换为SELECT DISTINCT ON SQL查询。这是   区别。对于正常的distinct()调用,数据库会比较每个   确定哪些行是不同的时,每行中的字段。为一个   distinct()调用指定的字段名称,数据库只会   比较指定的字段名称。

此外,您必须指定order_by,它不能通过时间戳:

q = FruitBasket.objects.distinct('fruit').order_by('fruit')
  

指定字段名称时,必须在中提供order_by()   QuerySet和order_by()中的字段必须以字段开头   distinct(),顺序相同。

     

例如,SELECT DISTINCT ON(a)为每个提供第一行   列a中的值。如果您没有指定订单,您将获得一些订单   任意行。

然而,values可能会让你更接近,如果你可以杀死这个要求:distinct / order_by具有相同顺序的相同值。

q = FruitBasket.objects.values('id', 'fruit', 'count').distinct('fruit').order_by('-id')
实际上,有时打破ORM更好。

SELECT id, fruit, count 
FROM FruitBasket
GROUP BY fruit  
ORDER BY id DESC

bad query所以这个查询不是魔术......

SELECT * FROM (SELECT id, fruit, count 
FROM FruitBasket
ORDER BY id DESC) t
GROUP BY t.fruit

better but still horrid这些更好但有点难看。

自行优化:

q = FruitBasket.objects.raw("SELECT * FROM (SELECT id, fruit, count FROM FruitBasket ORDER BY id DESC) t GROUP BY t.fruit")

答案 1 :(得分:1)

您可以尝试以下方法:

FruitBasket.objects.order_by('fruit', '-count').distinct('fruit')

就我而言,它适用于Django 2.1

答案 2 :(得分:1)

作为替代方案,如果您拥有固定(少量)的可能不同值,则可以使用多个查询(虽然不是最佳选择,但应该适用于小型项目):

available_fruits = ['banana', 'apple'] # can be also an extra query to extract distinct values
fruits = [FruitBasket.objects.filter(fruit=x).latest('id') for x in available_fruits ]

在我的情况下,它只有4个值,所以我可以进行4个简单而快速的查询。

答案 3 :(得分:1)

子查询可能会在这里帮助您, docs中的示例:

>>> from django.db.models import OuterRef, Subquery
>>> newest = Comment.objects.filter(post=OuterRef('pk')).order_by('-created_at')
>>> Post.objects.annotate(newest_commenter_email=Subquery(newest.values('email')[:1]))