由于您使用 Postgres,您可以使用它的Window Functions,它对一组与当前行有某种关联的表行执行计算。另一个好的信息在于您使用支持窗口函数(Django docs)的 Django2.x,它允许将 OVER 子句添加到 Querysets。
您的用例可以通过单一 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))
输出:
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
原始 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
窗口函数被声明为一个聚合函数,后面跟着一个 OVER 子句,它准确地指示了行是如何分组的。应用窗口函数的行组称为“分区”。
您会注意到我们按 'client' 字段对行进行了分组,您可以得出结论,在我们的示例中,我们将拥有两个分区。第一个分区将包含所有 'facebook' 条目,第二个分区将包含所有 'google' 条目。在其基本形式中,分区与普通的聚合函数组没有什么不同:只是一组被某些标准视为“相等”的行,并且该函数将应用于所有这些行以返回单个结果。
在您的示例中,我们可以使用 row_number 窗口函数,它只返回从 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))
输出:
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
您会注意到GROUP_SIZE 常量定义了每个交替组中的项目数。
附言
感谢您提出这个问题,因为它帮助我更好地理解了窗口函数。
编码愉快:)