使用 Django 查询集获取每个组的前 n 条记录

Get top n records for each group with Django queryset

我有一个像下面这样的模型 Table,

create table `mytable`
(
  `person` varchar(10),
  `groupname` int,
  `age` int
);

我想从每组中选出年龄最大的 2 个人。原始 SQL 问题和答案在这里 Whosebug 并且有效的解决方案之一是

SELECT
    person,
    groupname,
    age
FROM
(
    SELECT
        person,
        groupname,
        age,
        @rn := IF(@prev = groupname, @rn + 1, 1) AS rn,
        @prev := groupname
    FROM mytable
    JOIN (SELECT @prev := NULL, @rn := 0) AS vars
    ORDER BY groupname, age DESC, person
) AS T1
WHERE rn <= 2

您也可以在此处检查 SQL 输出 SQLFIDLE

我只是想知道如何在 Django 的视图中将此查询作为查询集来实现。

另一个具有类似输出的 SQL 将具有 window 函数,该函数用特定组名称中的行号注释每一行,然后您将在 HAVING 中过滤小于或等于 2 的行号条款。

在写django的时候does not support filtering based on window function result所以你需要在第一个查询中计算行并在第二个查询中过滤People

以下代码基于 similar question,但它实现了限制行数 return 每个 group_name

from django.db.models import F, When, Window
from django.db.models.functions import RowNumber

person_ids = {
    pk
    for pk, row_no_in_group in Person.objects.annotate(
        row_no_in_group=Window(
            expression=RowNumber(), 
            partition_by=[F('group_name')],
            order_by=['group_name', F('age').desc(), 'person']
        )
    ).values_list('id', 'row_no_in_group')
    if row_no_in_group <= 2
}
filtered_persons = Person.objects.filter(id__in=person_ids)

对于Persontable

的后续状态
>>> Person.objects.order_by('group_name', '-age', 'person').values_list('group_name', 'age', 'person')
<QuerySet [(1, 19, 'Brian'), (1, 17, 'Brett'), (1, 14, 'Teresa'), (1, 13, 'Sydney'), (2, 20, 'Daniel'), (2, 18, 'Maureen'), (2, 14, 'Vincent'), (2, 12, 'Carlos'), (2, 11, 'Kathleen'), (2, 11, 'Sandra')]>

上面的查询 return

>>> filtered_persons.order_by('group_name', '-age', 'person').values_list('group_name', 'age', 'person')
<QuerySet [(1, 19, 'Brian'), (1, 17, 'Brett'), (2, 20, 'Daniel'), (2, 18, 'Maureen')]>