通过交替值来订购查询集

ils*_*005 10 django postgresql django-models django-queryset

我有以下型号:

class Entry(models.Model):
    name = models.Charfield(max_length=255)
    client = models.Charfield(max_length=255)
Run Code Online (Sandbox Code Playgroud)

client是客户端的名称,可能有像facebook,google等等的值.

是否可以对查询集进行排序,以便结果交替出现client?的值?我的期望是这样的:

Entry.objects.order_by('alternate client') --> 

| client   | name   |
| google   | robert |
| facebook | linda  |
| google   | kate   | 
| facebook | jack   |
| google   | nina   |
| facebook | pierre |    
Run Code Online (Sandbox Code Playgroud)

如果有帮助,我正在使用django2.x和postgres.

编辑:

一些额外的信息/要求.

  • 我有大约10到20个不同的客户
  • Entry还有一个createdDateField.如果可能的结果也应按日期排序
  • 我想对Entry使用分页,所以解决方案应该是使用django的ORM

T.T*_*kic 14

由于您使用Postgres,因此您可以使用其窗口函数,该函数在一组与当前行有某种关联的表行中执行计算.另一个好的信息依赖于你使用Django2.x,它支持允许添加子句的Window Functions(Django docs).OVERQuerysets

您可以使用单个ORM查询解决您的用例,例如:

from django.db.models.expressions import Window
from django.db.models.functions import RowNumber
from django.db.models import F

results = Entry.objects.annotate(row_number=Window(
    expression=RowNumber(),
    partition_by=[F('client')],
    order_by=F('created').desc())
).order_by('row_number', 'client')

for result in results:
    print('Id: {} - client: {} - row_number {}'.format(result.id, result.client, result.row_number))
Run Code Online (Sandbox Code Playgroud)

输出:

Id: 12 - client: facebook - row_number 1
Id: 13 - client: google - row_number 1
Id: 11 - client: facebook - row_number 2
Id: 8 - client: google - row_number 2
Id: 10 - client: facebook - row_number 3
Id: 5 - client: google - row_number 3
Id: 9 - client: facebook - row_number 4
Id: 3 - client: google - row_number 4
Id: 7 - client: facebook - row_number 5
Id: 2 - client: google - row_number 5
Id: 6 - client: facebook - row_number 6
Id: 1 - client: google - row_number 6
Id: 4 - client: facebook - row_number 7
Run Code Online (Sandbox Code Playgroud)

原始SQL看起来像:

SELECT 
"orm_entry"."id",
"orm_entry"."name",
"orm_entry"."client",
"orm_entry"."created",
ROW_NUMBER() OVER (PARTITION BY "orm_entry"."client" ORDER BY "orm_entry"."created" DESC) AS "row_number" 
FROM "orm_entry" 
ORDER BY "row_number" ASC, "orm_entry"."client" ASC
Run Code Online (Sandbox Code Playgroud)

窗口函数被声明为聚合函数,后跟一个OVER子句,该子句准确指示行的分组方式.应用窗函数的行组称为"分区".

您可以注意到我们按"客户"字段对行进行了分组,您可以得出结论,在我们的示例中,我们将有两个分区.第一个分区将包含所有" facebook"条目,第二个分区将包含所有"google"条目.在其基本形式中,分区与普通聚合函数组没有区别:只是一组被某些条件视为"相等"的行,并且该函数将应用于所有这些行以返回单个结果.

在你的例子中,我们可以使用row_numberwindow函数,它只返回从1开始的分区中当前行的索引.这有助于我建立交替输出order_by('row_number', 'client').

附加信息:

如果您想要实现这样的订单:

'facebook','facebook', 'google','google','facebook','facebook','google','google'

要么

'facebook','facebook','facebook','google','google','google','facebook', 'facebook','facebook'

您将需要对上一个查询进行一个小的数学相关修改,如:

GROUP_SIZE = 2
results = Entry.objects.annotate(row_number=Window(
    expression=RowNumber(),
    partition_by=[F('client')],
    order_by=F('created').desc())
).annotate(row_number=(F('row_number') - 1)/GROUP_SIZE + 1).order_by('row_number', 'client')

for result in results:
    print('Id: {} - client: {} - row_number {}'.format(result.id, result.client, result.row_number))
Run Code Online (Sandbox Code Playgroud)

输出:

Id: 12 - client: facebook - row_number 1
Id: 11 - client: facebook - row_number 1
Id: 8 - client: google - row_number 1
Id: 13 - client: google - row_number 1
Id: 10 - client: facebook - row_number 2
Id: 9 - client: facebook - row_number 2
Id: 3 - client: google - row_number 2
Id: 5 - client: google - row_number 2
Id: 7 - client: facebook - row_number 3
Id: 6 - client: facebook - row_number 3
Id: 1 - client: google - row_number 3
Id: 2 - client: google - row_number 3
Id: 4 - client: facebook - row_number 4
Run Code Online (Sandbox Code Playgroud)

您可以注意到GROUP_SIZE常量定义了每个交替组中的项目数.

PS

感谢您提出这个问题,因为它帮助我更好地理解了窗口函数.

快乐编码:)