【问题标题】:Django: Record with max elementDjango:使用最大元素记录
【发布时间】:2012-04-07 22:59:14
【问题描述】:

我有一个名为“student”的数据库表,其中有一列名为“marks”。我想要数学最高分的学生记录。使用order_by()[0] 有一个简单的解决方案:

Student.objects.filter(subject='Maths').order_by('-marks')[0]

但这会对表格进行排序,然后获取第一条记录。如果我的表很大,这是多余的,因为我只需要最大记录。有什么方法可以不排序就得到最大值?

我想要整个对象,而不仅仅是最大值。

谢谢阿努杰

【问题讨论】:

    标签: django django-models


    【解决方案1】:

    如果你的意思是你需要一条数学最高分的记录,我认为使用 SQL LIMIT 更明显和更可取:

    Student.objects.filter(subject='Maths').order_by('-marks')[:1].get()
    

    SQL 将是这样的:

    SELECT * FROM student WHERE subject = 'Maths' ORDER BY marks DESC LIMIT 1
    

    但请记住,可能有几个学生得分最高,你会得到一个随机的。

    【讨论】:

      【解决方案2】:
      from django.db.models import Max
      
      temp = Student.objects.filter(subject='Math').aggregate(Max('marks'))
      record = Student.objects.filter(Q(subject='Math') & Q(subject=temp['marks__max']))
      

      【讨论】:

        【解决方案3】:

        所需的 SQL 如下所示:

        SELECT *
        FROM STUDENT
        WHERE marks = (SELECT MAX(marks) FROM STUDENT)
        

        要通过 Django 执行此操作,您可以使用 aggregation API

        max_marks = Student.objects.filter(
            subject='Maths'
        ).aggregate(maxmarks=Max('marks'))['maxmarks']
        Student.objects.filter(subject='Maths', marks=max_marks)
        

        不幸的是,这个查询实际上是两个查询。执行最大标记聚合,将结果拉入 python,然后传递给第二个查询。 (令人惊讶的是)没有办法传递一个查询集,它只是一个没有分组的聚合,即使它应该是可能的。我要开一张票,看看如何解决这个问题。

        编辑:

        可以通过单个查询来做到这一点,但这不是很明显。我在其他地方没见过这种方法。

        from django.db.models import Value
        
        max_marks = (
            Student.objects
                   .filter(subject='Maths')
                   .annotate(common=Value(1))
                   .values('common')
                   .annotate(max_marks=Max('marks'))
                   .values('max_marks')
        )
        
        Student.objects.filter(subject='Maths', marks=max_marks)
        

        如果你在 shell 中打印这个查询,你会得到:

        SELECT 
               "scratch_student"."id", 
               "scratch_student"."name", 
               "scratch_student"."subject", 
               "scratch_student"."marks" 
          FROM "scratch_student" 
         WHERE ( 
               "scratch_student"."subject" = Maths 
           AND "scratch_student"."marks" = (
               SELECT 
                      MAX(U0."marks") AS "max_marks" 
                 FROM "scratch_student" U0 
                WHERE U0."subject" = Maths))
        

        在 Django 1.11 上测试(目前处于 alpha 阶段)。这是通过按常数 1 对注释进行分组来实现的,每一行都将分组。然后我们从选择列表中删除这个分组列(第二个values()。Django(现在)知道足以确定分组是多余的,并消除它。留下一个带有我们需要的确切 SQL 的查询。

        【讨论】:

        • @ChadVernon,它们被称为 F() 表达式。它们允许您使用另一列的值。 docs.djangoproject.com/en/dev/topics/db/queries/…
        • 这不起作用。生成的 SQL 类似于:SELECT *, MAX("mark") AS "max_mark" FROM STUDENT HAVING "STUDENT"."mark" = (MAX("STUDENT"."marks")),它只是选择所有学生。
        • 你说得对,我应该在写答案之前测试一下。更新一个有效的答案。
        【解决方案4】:

        对于一个简单的数据库表,理论上数据库在没有首先排序的情况下不可能为您检索最大值。想想看,数据库不查看每一行,怎么知道哪个是最大值?

        当然,这是一个非常幼稚的设置。幸运的是,您有两种选择:

        1. 使用索引。如果您在该列上创建索引,排序通常可以利用索引 - 为您节省全表扫描。

        2. 标准化(又名预计算)。在某处创建另一个存储最大值的表,并确保每次添加/修改/删除学生对象时检查/更新它。

        在不了解更多需求的情况下,强烈建议使用索引。

        查看:https://docs.djangoproject.com/en/dev/ref/models/fields/#db-index

        【讨论】:

        • 这个问题似乎更多地是关于 Django ORM 而不是关于数据库或表结构,您的答案是针对的。
        • 理论上,找到最大值是一个 o(n) 操作——你只需要访问每个元素一次,这与排序不同。对于排序,您需要进行 o(nlogn) 比较。
        • @abhaga 在我看来,数据库的主要目标是允许快速数据访问。通过创建索引,数据库保持排序,因此访问最大值(或最小值或偏移量)是 O(1) 操作。
        • @rushy 我同意使用索引是一个有用的解决方案。我指的是答案的第一行,说没有先排序就无法找到最大值。此外 index 更像是一个随机访问密钥,它可以快速告诉您在哪里查找。它不会保持表格排序。
        【解决方案5】:

        这个问题可能对你有帮助: How to do SELECT MAX in Django?

        只使用聚合。

        from django.db.models import Max
        Student.objects.filter(subject='Math').aggregate(Max('marks'))
        

        未经测试,但应该可以工作。 :)

        【讨论】:

        • OP 想要整行,而不仅仅是最大值,“我想要数学最高分的学生记录” - OP
        猜你喜欢
        • 2023-04-02
        • 2012-10-29
        • 2018-07-11
        • 2013-12-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-07-16
        • 2010-11-10
        相关资源
        最近更新 更多