使用 distinct 后如何执行 order_by?

Posted

技术标签:

【中文标题】使用 distinct 后如何执行 order_by?【英文标题】:How to execute an order_by after using distinct? 【发布时间】:2017-06-01 17:22:03 【问题描述】:

我想在基于两个字段获取唯一结果后对结果进行排序。我遇到的问题是我不能在 Django 中使用两个 order_by。 Django clears the previous order_by() whenever you use this function.

每次 order_by() 调用都会清除之前的所有订单。

模型示例:

class Product(models.Model):
    price = models.FloatField()
    name = models.CharField(max_length=100)
    group = models.CharField(max_length=100)

我想获得同组中最低的产品。然后,我想按价格对它们进行排序。像这样的:

Product.objects.order_by('group','price').distinct('group').order_by('price')
Product.objects.order_by('group','price').distinct('group').order_by('-price')

问题是,如果只是在第一个订单中使用,我会得到不同的产品。

--

编辑

表格示例:

id | name | group | price
0  | aaa  | aaa   | 10.0
1  | aaa  | aaa   | 1.0
2  | aaa  | aaa   | 2.0
3  | aaa  | aaa   | 1.0
4  | bbb  | bbb   | 2.0
5  | bbb  | bbb   | 2.1
6  | bbb  | bbb   | 10.0

按价格排序:

1  | aaa  | aaa  | 1.0
4  | bbb  | bbb  | 2.0

按-价格排序:

4  | bbb  | bbb  | 2.0
1  | aaa  | aaa  | 1.0

我遇到的问题是,当我使用 order_by 排序时,返回的产品是不同的。

在sql语句中会是这样的:

SELECT * 
FROM product
WHERE id IN (
  SELECT DISTINCT ON 
  (group) id
  FROM product
  ORDER BY group ASC, price ASC
)
ORDER BY price DESC

我正在使用 PostgreSQL

【问题讨论】:

实际上,我无法完全理解您在这里想要做什么。您能否提供您尝试获取的 sql 查询? 您是否试图获得每组的最低价格,并按价格排序? 如果你砍掉第二个order_by 子句,第一个order_by 子句的顺序不是简单地保留了吗? @emre,我添加了 SQL 查询。 @SomeSillyName,没错。 【参考方案1】:

好的,这就是我所做的:

products = Product.objects.order_by('group','price').distinct('group')
result = Product.objects.filter(id__in=products).order_by('-price')

我不认为这是最有效的方法,但它正在工作......

【讨论】:

【参考方案2】:

我遇到了同样的问题并找到了另一个解决方案。在使用 sorted_by 后跟 distinct 向 DB 发出第一个请求后,您可以:

import operator

products = Product.objects.order_by('group','price').distinct('group')
result = sorted(products, key=operator.attrgetter('price'), reverse=True)

此解决方案更好,因为您没有向数据库发出第二次请求。

【讨论】:

这是解决 Django ORM 问题对值进行两次排序的好方法 如果它是一个小的查询集,那很好,但如果它很大,那么在内存中排序不是最好的方法,因为 a.性能,和 b.你不能分页。【参考方案3】:

我喜欢 Alexander 使用 sorted 的想法。我将排序后的函数放入 models.QuerySet 的子类中,然后相应地设置我的 ProductManager。这允许您将排序的方法链接到您的查询集。

问题:这会返回一个列表而不是查询集,因此您在排序后不能使用任何查询集方法。

相关模型字段排序:您可以通过使用点运算符 (key='price.other') 传递跨越查找的键来按相关模型字段排序

manager.py
class ProductQuerySet(models.QuerySet):
    def sorted(self, key, reverse=False):
        """ Returns a list (rather than queryset) of Djano model instances """
        return sorted(self, key=operator.attrgetter(key), reverse=reverse)

    def current(self):
        return self.order_by('group', 'price').distinct('group')

class ProductManagerBase(models.Manager):
   pass

ProductManager = ProductManagerBase.from_queryset(ProductQuerySet)

models.py
...
objects = ProductManager()

**查询

qs = (Product
     .current()
     .sorted('price', reverse=True))

【讨论】:

以上是关于使用 distinct 后如何执行 order_by?的主要内容,如果未能解决你的问题,请参考以下文章

如何在同一个 SELECT 语句中使用 DISTINCT 和 ORDER BY?

如何在 SQL Server 中使用带有框架的窗口函数执行 COUNT(DISTINCT)

使用 distinct 和 top 子句执行查询需要更多时间

如何在同一个SELECT语句中使用DISTINCT和ORDER BY?

SQL中 oderby和distinct哪一个先执行

SQL中distinct的用法