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

如何在Sequelize中按主题获取各用户的最佳游戏会话

按主题获取用户最高分排行榜的Sequelize优化实现

需求:基于Node.js+Sequelize开发答题游戏后端,实现按主题获取排行榜功能,每个用户仅返回该主题下最高分的游戏会话,需包含username、score、count_indicators、errors字段。

以下是两种基于Sequelize原生方法的优化实现,替代原有的多子查询方案:

方案一:使用窗口函数(推荐,仅一次查询)

利用数据库窗口函数ROW_NUMBER()按用户分组、分数降序排序,直接筛选每个用户的最高分会话,适合PostgreSQL、MySQL 8+等支持窗口函数的数据库:

async getAllBestPlayByTheme(req, res) { 
  try {
    const theme_id = req.params.id;
    const theme = await Theme.findByPk(theme_id);
    if(!theme){
      return res.status(404).json({error: 'Theme not found'});
    }

    const bestPlays = await Play.findAll({
      attributes: [
        'score', 'errors', 'count_indicators',
        // 按用户分组,分数降序生成行号
        [Sequelize.literal('ROW_NUMBER() OVER (PARTITION BY "Play"."user_id" ORDER BY "Play"."score" DESC)'), 'row_num']
      ],
      include: [{
        model: User,
        as: 'player',
        attributes: ['username']
      }],
      where: { theme_id },
      // 保留每个用户的第一条记录(最高分)
      having: Sequelize.where(Sequelize.literal('row_num'), '=', 1),
      order: [['score', 'DESC']],
      nest: true
    });

    // 移除行号字段,格式化输出
    const formattedResult = bestPlays.map(item => {
      const { row_num, ...rest } = item.toJSON();
      return rest;
    });

    if (formattedResult.length === 0) {
      return res.status(404).json({ message: "Score not found" });
    }
    res.json(formattedResult);
  } catch (error) {
    console.error('Error in bestPlays : ', error);
    res.status(500).json({ message: "Error to get score" });
  }
}

方案二:子查询关联(兼容旧版数据库)

先查询每个用户在指定主题下的最高分,再关联Play表筛选对应会话,适合不支持窗口函数的旧版数据库:

async getAllBestPlayByTheme(req, res) { 
  try {
    const theme_id = req.params.id;
    const theme = await Theme.findByPk(theme_id);
    if(!theme){
      return res.status(404).json({error: 'Theme not found'});
    }

    // 子查询:获取每个用户的最高分
    const userMaxScores = await Play.findAll({
      attributes: ['user_id', [Sequelize.fn('MAX', Sequelize.col('score')), 'maxScore']],
      where: { theme_id },
      group: ['user_id'],
      raw: true
    });

    // 构建匹配条件:用户ID+最高分+主题ID
    const matchConditions = userMaxScores.map(score => ({
      user_id: score.user_id,
      score: score.maxScore,
      theme_id
    }));

    // 查询对应会话并关联用户信息
    const bestPlays = await Play.findAll({
      attributes: ['score', 'errors', 'count_indicators'],
      include: [{
        model: User,
        as: 'player',
        attributes: ['username']
      }],
      where: { [Sequelize.Op.or]: matchConditions },
      order: [['score', 'DESC']],
      nest: true
    });

    if (bestPlays.length === 0) {
      return res.status(404).json({ message: "Score not found" });
    }
    res.json(bestPlays);
  } catch (error) {
    console.error('Error in bestPlays : ', error);
    res.status(500).json({ message: "Error to get score" });
  }
}

方案对比

  • 方案一:仅需一次数据库查询,性能更优,代码简洁,依赖数据库对窗口函数的支持。
  • 方案二:兼容性更强,适合旧版数据库,逻辑直观但需两次查询。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 21:43:19