使用 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)
对于Person
table
的后续状态
>>> 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')]>
我有一个像下面这样的模型 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)
对于Person
table
>>> 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')]>