【问题标题】:Get top n records for each group with Django queryset使用 Django 查询集获取每个组的前 n 条记录
【发布时间】:2021-03-27 03:47:03
【问题描述】:

我有一个类似下表的模型,

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

我想从每个组中选出 2 位最年长的人。原始的 SQL 问题和答案在这里StackOverflow 和其中一个可行的解决方案是

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 的视图中将这个查询实现为查询集。

【问题讨论】:

    标签: django django-orm


    【解决方案1】:

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

    在编写 django does not support filtering based on window function result 时,您需要在第一个查询中计算行并在第二个查询中过滤 People

    以下代码基于 similar question,但它实现了每个 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 表的以下状态

    >>> 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')]>
    

    上面的查询返回

    >>> 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')]>
    

    【讨论】:

      猜你喜欢
      • 2013-12-31
      • 1970-01-01
      • 2022-12-07
      • 2020-06-14
      • 2012-03-12
      • 2021-09-01
      相关资源
      最近更新 更多