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

如何用Flask-SQLAlchemy实现15个不同用户分数的升序查询

问题解决思路与代码修改

你的核心需求是从符合条件的分数记录中,每个用户仅保留最低分,再按分数升序取前15条。原代码的问题在于:distinct(Scores.user_id)配合order_by(Scores.user_id)只会保留每个用户的任意一条记录(而非最低分),后续的分数排序只是对这些随机记录重新排列,无法满足需求。以下是两种可行的解决方案:


方案一:窗口函数(推荐,精准控制取数逻辑)

利用PostgreSQL的窗口函数row_number(),给每个用户的分数按「分数升序、记录ID升序」编号,取编号为1的记录(即该用户的最低分,若有多个相同最低分则取最早创建的那条),之后再整体排序取前15。

修改后的完整代码:

from sqlalchemy import func  # 需导入func模块

@blueprint.route("/")
def index():
    diff_arg = request.args.get("diff", 0)
    ver_arg = request.args.get("ver", GAME_VERSION)
    user_arg = request.args.get("user", None)

    # 先过滤版本和难度,缩小数据集提升查询效率
    scores = Scores.query
    if ver_arg:
        scores = scores.filter_by(version=ver_arg)
    scores = scores.filter_by(difficulty=diff_arg)

    if not user_arg:
        # 窗口函数:按用户分组,给每组内的分数按「分数升序、ID升序」编号
        row_num = func.row_number().over(
            partition_by=Scores.user_id,
            order_by=[Scores.score.asc(), Scores.id.asc()]
        )
        # 生成包含编号的子查询
        subquery = scores.add_columns(row_num.label('row_num')).subquery()
        # 关联子查询,仅保留每个用户编号为1的记录(最低分)
        scores = Scores.query.join(
            subquery,
            (Scores.id == subquery.c.id) & (subquery.c.row_num == 1)
        )
    else:
        if user := Users.query.filter_by(username=user_arg).first():
            scores = scores.filter_by(user_id=user.id)
        else:
            abort(404, "User not found")

    # 按分数升序排序,取前MAX_TOP_SCORES条(即15条)
    scores = scores.order_by(Scores.score.asc()).limit(MAX_TOP_SCORES).all()

    return render_template(
        "views/scores.html",
        scores=scores,
        diff=int(diff_arg),
        ver=ver_arg,
        user=user_arg
    )

方案二:分组聚合+关联(适合仅需核心字段的场景)

先通过group_by和func.min()获取每个用户的最低分,再关联原表拿到完整记录。若用户存在多个相同最低分,需额外添加去重逻辑:

from sqlalchemy import func

@blueprint.route("/")
def index():
    diff_arg = request.args.get("diff", 0)
    ver_arg = request.args.get("ver", GAME_VERSION)
    user_arg = request.args.get("user", None)

    scores = Scores.query
    if ver_arg:
        scores = scores.filter_by(version=ver_arg)
    scores = scores.filter_by(difficulty=diff_arg)

    if not user_arg:
        # 子查询:按用户分组,获取每个用户的最低分
        subquery = scores.with_entities(
            Scores.user_id,
            func.min(Scores.score).label('min_score')
        ).group_by(Scores.user_id).subquery()
        # 关联原表,拿到对应用户最低分的记录
        scores = Scores.query.join(
            subquery,
            (Scores.user_id == subquery.c.user_id) & (Scores.score == subquery.c.min_score)
        ).distinct(Scores.user_id)  # 处理用户有多个相同最低分的情况
    else:
        if user := Users.query.filter_by(username=user_arg).first():
            scores = scores.filter_by(user_id=user.id)
        else:
            abort(404, "User not found")

    scores = scores.order_by(Scores.score.asc()).limit(MAX_TOP_SCORES).all()

    return render_template(
        "views/scores.html",
        scores=scores,
        diff=int(diff_arg),
        ver=ver_arg,
        user=user_arg
    )

原代码失效原因

PostgreSQL的distinct(col)行为完全依赖前置的order_by:它会按照order_by的顺序保留每组的第一条记录。你之前先按user_id排序再去重,拿到的是每个用户的任意一条记录(而非最低分),后续的分数排序只是对这些随机记录重新排列,自然不符合需求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 08:23:12