我如何 return 仅显示学生按日期收到的最后分数?

How do I return only the last score a student has received by date?

我一直在尝试弄清楚如何收集学生在年度测试中获得的最后分数,但我能够想出的唯一有希望的方法是使用 MAX 函数。一切都出错的地方是当我必须对结果中 returning 的其他字段进行 GROUP BY 时。如果我可以从 GROUP BY 中删除 Studenttestscore_Numscore 字段,我认为它会起作用,但我无法这样做。我如何才能根据 Test_Date?

将此写入 return 数据库中每个学生收到的最后一个分数

我在下面写的方式 returns 每个学生的所有分数都不同。我相信如果它允许我从 GROUP BY 中删除 Studenttestscore_Numscore,它会起作用,但它不会。

现在,因为我必须将它包括在内才能使 MAX 函数起作用,所以我得到了学生在本次年度评估中取得的所有分数,除非它与之前的分数相同。

谢谢

SELECT
    ps.students.student_number,
    ps.test.name                  AS test_name,
    ps.studenttestscore.numscore  AS Studenttestscore_Numscore,
    MAX(ps.studenttest.test_date)      AS test_date
    
FROM
    ps.studenttestscore
    INNER JOIN ps.studenttest ON ps.studenttest.id = ps.studenttestscore.studenttestid
    INNER JOIN ps.students ON ps.studenttest.studentid = ps.students.id
    INNER JOIN ps.testscore ON ps.studenttestscore.testscoreid = ps.testscore.id
    INNER JOIN ps.test ON ps.test.id = ps.testscore.testid
    
WHERE
        ps.test.id = 269
    AND ps.testscore.id = 439
    
GROUP BY 
    ps.students.student_number,
    ps.test.name,
    ps.studenttestscore.numscore

我通常喜欢用 row_number() 来处理这样的事情。 我为具有多个记录的数据编写了一个子查询,我试图提取最新的记录,并相应地包含一个 partitioned/ordered 的 rownum,然后在 rownum=1 上加入。不需要分组。

因此对于您的查询,要提取每个 student/test ID 的最新记录,它最终会是:

SELECT
    ps.students.student_number,
    ps.test.name                  AS test_name,
    ps.studenttestscore.numscore  AS Studenttestscore_Numscore,
    st.test_date
    
FROM
    ps.studenttestscore
    INNER JOIN (
        select *, rownum = row_number()over(partition by studentid, testid order by test_date desc)
        from ps.studenttest
    ) st ON st.id = ps.studenttestscore.studenttestid and st.rownum = 1
    INNER JOIN ps.students ON st.studentid = ps.students.id
    INNER JOIN ps.testscore ON ps.studenttestscore.testscoreid = ps.testscore.id
    INNER JOIN ps.test ON ps.test.id = ps.testscore.testid
    
WHERE
        ps.test.id = 269

这是假设 testid 字段在您的 ps.studenttest table 中可用,因为它需要包含在分区中,但我不知道您的架构,所以您如果不是这样,则必须相应地进行修改。

您可以使用 DENSE_RANK() 分析函数中的 returning 值,其中按 ps.students.student_number 列应用分组,同时按 ps.studenttest.test_date 列,以便选择最新的匹配记录并删除当前的 GROUP BY 子句,例如

SELECT s.student_number,
       t.name AS test_name,
       sts.numscore AS Studenttestscore_Numscore,
       st.test_date,
       DENSE_RANK() OVER 
         (PARTITION BY s.student_number ORDER BY st.test_date DESC) AS rank_latest_first
  FROM ps.studenttestscore sts
  JOIN ps.studenttest st
    ON st.id = sts.studenttestid
  JOIN ps.students s
    ON st.studentid = s.id
  JOIN ps.testscore ts
    ON sts.testscoreid = ts.id
  JOIN ps.test t
    ON t.id = ts.testid
 WHERE t.id = 269
   AND ts.id = 439
 ORDER BY rank_latest_first
 FETCH FIRST 1 ROW WITH TIES

其中 WITH TIES 选项提供 return 多行,只要单个学生的最新日期不止一个。

DB 的版本应该是 12c+ 以便能够使用 FETCH 子句.

使用MAX(...) KEEP (DENSE_RANK LAST ORDER BY ...)仅保留一列的最后一个值,然后获取另一列的最大值:

SELECT s.student_number,
       MAX(t.name) AS test_name,
       MAX(sts.numscore) KEEP (DENSE_RANK LAST ORDER BY st.test_date)
         AS Studenttestscore_Numscore,
       MAX(st.test_date) AS test_date
FROM   ps.studenttestscore sts
       INNER JOIN ps.studenttest st ON st.id = sts.studenttestid
       INNER JOIN ps.students s     ON st.studentid = s.id
       INNER JOIN ps.testscore ts   ON sts.testscoreid = ts.id
       INNER JOIN ps.test t         ON t.id = ts.testid
WHERE  t.id = 269
AND    ts.id = 439
GROUP BY 
       s.student_number,
       t.id

或使用 ROW_NUMBER 分析函数对每个分区的行进行排序,然后过滤以仅获取最新的行:

SELECT student_number,
       name AS test_name,
       numscore AS Studenttestscore_Numscore,
       test_date
FROM   (
  SELECT s.student_number,
         t.name,
         sts.numscore,
         st.test_date,
         ROW_NUMBER() OVER (
           PARTITION BY s.student_number, t.id
           ORDER BY st.test_date DESC
         ) AS rn
  FROM   ps.studenttestscore sts
         INNER JOIN ps.studenttest st ON st.id = sts.studenttestid
         INNER JOIN ps.students s     ON st.studentid = s.id
         INNER JOIN ps.testscore ts   ON sts.testscoreid = ts.id
         INNER JOIN ps.test t         ON t.id = ts.testid
  WHERE  t.id = 269
  AND    ts.id = 439
)
WHERE  rn = 1;