You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何不使用for循环实现Django查询:获取用户每道题的最终提交记录

Django 单查询实现竞赛用户最终提交列表获取

针对获取指定竞赛下所有用户每道题最近一次提交的需求,可通过Django内置的窗口函数或子查询实现单SQL查询,完全替代嵌套循环,性能提升显著,实现代码如下:

方案1:窗口函数实现(推荐,Django 2.0+支持)

仅执行1次数据库查询,性能最优:

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

def list_final_submissions(contest_id):
    # 读取竞赛实例
    contest = Contest.objects.get(id=contest_id)
    # 筛选属于当前竞赛范围内的所有提交(题目属于竞赛、提交者是竞赛参与者)
    contest_submissions = Submission.objects.filter(
        problem__in=contest.problems.all(),
        participant__in=contest.participants.all()
    )
    # 按「用户+题目」分组,每组内按提交时间倒序排行号
    ranked_submissions = contest_submissions.annotate(
        row_num=Window(
            expression=RowNumber(),
            partition_by=[F('participant_id'), F('problem_id')],
            order_by=F('submitted_time').desc()
        )
    )
    # 只取每组行号为1的记录,即对应每个用户每道题的最近一次提交
    return ranked_submissions.filter(row_num=1)

方案2:子查询实现(兼容低版本Django)

如果你的Django版本低于2.0不支持窗口函数,可改用子查询方案:

from django.db.models import Subquery, OuterRef

def list_final_submissions(contest_id):
    contest = Contest.objects.get(id=contest_id)
    # 子查询:匹配每个用户+题目组合,返回最新一条提交的ID
    latest_sub_id = Submission.objects.filter(
        participant_id=OuterRef('participant_id'),
        problem_id=OuterRef('problem_id'),
        problem__in=contest.problems.all(),
        participant__in=contest.participants.all()
    ).order_by('-submitted_time').values('id')[:1]
    # 主查询过滤出所有最新提交记录
    return Submission.objects.filter(id__in=Subquery(latest_sub_id)).distinct()

优化建议

  • 可给Submission模型的submitted_time、participant_id、problem_id字段加联合索引,数据量较大时查询效率会有明显提升。
  • 如果存在同一用户同一题同一时间提交多次的极端场景,可在排序规则中追加id倒序,确保取到的是最后生成的提交记录:order_by=[F('submitted_time').desc(), F('id').desc()]

内容的提问来源于stack exchange,提问作者eva

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.04 09:27:00