sql—使用nodejs app的sequelize函数在postgresql中实现两级聚合

hrirmatl  于 2021-07-26  发布在  Java
关注(0)|答案(2)|浏览(281)

大家好,请耐心听我说。我正在使用postgresql、sequelize、express和nodejs创建后端。我想知道这些原始查询代码行是否可以使用sequelize模型findall函数实现。
首先,我要做的是计算这些学生的总分。这是一些表及其关系。

Student                               Level
| student_id |    name    | level_id |       | level_id | level_name |
|:----------:|:----------:|:--------:|       |:--------:|:----------:|
|      1     |    John    |     1    |   >   |     1    |   Rookie   |
|      2     |    Jane    |     2    |       |     2    |   Expert   |

                    v

              StudentQuiz
|   quiz_id  | student_id |   score  |
|:----------:|:----------:|:--------:|
|      1     |      1     |    40    |
|      1     |      1     |    100   |
|      2     |      1     |    80    |
|      1     |      2     |    100   |
|      2     |      2     |    100   |

如果我运行下面的代码行。

SELECT table2.student_id,
          s.canvasser_name,
          l.level_name,
          table2.total_score
   FROM (SELECT table1.student_id,
                sum(table1.max_score) total_score
         FROM (SELECT sq.student_id,
               max(sq.score) max_score
               FROM public.StudentQuiz sq
               GROUP BY sq.quiz_id, sq.student_id) table1
         GROUP BY table1.student_id) table2
   INNER JOIN public.Student s
           ON s.student_id = table2.student_id
   INNER JOIN public.Level l
           ON l.level_id = s.level_id
   ORDER BY table2.total_score DESC
   LIMIT 10;

我要买这样的东西。

| student_id |    name    |  level   |   total_score  |
|:----------:|:----------:|:--------:|:--------------:|
|      1     |    John    |  Rookie  |       180      |
|      2     |    Jane    |  Expert  |       200      |

请注意,如果找到多个具有相同id的测验,我将选择最高分数。
不管怎样,我想用sequelize内置函数来实现它。我一直想做的就是这样。

const result = await StudentQuiz.findAll({
      attributes: ['studentId', [sequelize.fn('sum', sequelize.fn('max', sequelize.col('score'))), 'totalPrice'], 'quizId'],
      group: 'studentId',
      include: [
        {
          model: Student,
          include: [{
            model: Level
          }],
        },
      ],
      offset: 0,
      limit: 10
    });

上面的代码抛出一条错误消息,即“聚合函数调用不能嵌套”。
任何帮助都将不胜感激。谢谢您。
p、 我知道我可以使用sequelize.query()函数来使用显示的第一个代码块,但这不是重点。

jtjikinw

jtjikinw1#

sequelize不打算使用模型处理复杂的聚合。它的主要目标是为crud操作提供模型。
要在这种情况下使用模型,您可以使用模型定义来获取模式、表名和字段,以便在不知道确切字段名的情况下动态构建查询。

06odsfpq

06odsfpq2#

我在不使用任何原始查询的情况下找到了解决方案,不过我需要得到两个表,即studentquick和student that coupled by level。这是我的答案。

// Finding maximum score and group it based on studentId and quizId
    const maxScoreList = await StudentQuiz.findAll({
      attributes: ['studentId', 'quizId', [sequelize.fn('max', sequelize.col('score')), 'maxScore']],
      group: ['studentId', 'quizId'],
      order: ['studentId', 'quizId'],
      raw: true
    });

    // Calculating total score for the same student for each quiz recorded
    const scoreArray = [maxScoreList.shift()];
    let index = 0;
    const unfilteredStudentId = maxScoreList.map((item) => {
      if (scoreArray[index].studentId !== item.studentId) {
        scoreArray.push(item);
        index += 1;
      }
      scoreArray[index].maxScore += item.maxScore;
      return item.studentId;
    });

    // Filtering studentId that show up more than one time
    const extractedStudentId = [...new Set(unfilteredStudentId)];

    // Finding student based on studentId inside extractedStudentId array
    const student = await Student.findAll({
      where: { id: extractedStudentId },
      attributes: ['id', 'canvasserId', 'canvasserName', 'canvasserImageUrl'],
      include: {
        model: Level,
        attributes: [['level_name', 'level'], ['icon_url', 'level_image_url']]
      },
      order: ['id'],
      raw: true,
      nest: true
    });

    // Combining total score list to student list
    const rankList = student.map((item, idx) => ({ ...item, totalScore: scoreArray[idx] }));

有了这么多的复杂性,我同意使用原始查询是目前为止解决这种情况的最佳方法。

相关问题