Sql 按列分组,选择最近的值

Sql 按列分组,选择最近的值,sql,tsql,Sql,Tsql,我正在对一个表执行查询,该表跟踪学生的测试结果。考试由多个部分组成,每个部分的分数都有一列。每一行都是学生所做测试的一个实例。这些部分可以一次全部获取,也可以分为多个尝试。例如,一名学生今天可以选修一个部分,明天选修其余部分。此外,学生可以重考考试的任何部分 样本学生: 编辑: 有很多好的解决方案。在选择答案之前,我想在下周对每个问题进行更多的实验。谢谢大家 将以下内容使用到最长时间如何 从表\u NAME中选择MaxDateTake 其中StudentID=1 您可以在子查询中使用它来获得一行

我正在对一个表执行查询,该表跟踪学生的测试结果。考试由多个部分组成,每个部分的分数都有一列。每一行都是学生所做测试的一个实例。这些部分可以一次全部获取,也可以分为多个尝试。例如,一名学生今天可以选修一个部分,明天选修其余部分。此外,学生可以重考考试的任何部分

样本学生:

编辑:
有很多好的解决方案。在选择答案之前,我想在下周对每个问题进行更多的实验。谢谢大家

将以下内容使用到最长时间如何

从表\u NAME中选择MaxDateTake 其中StudentID=1

您可以在子查询中使用它来获得一行,如

从表_NAME中选择WritingSection 其中StudentID=1和DateTake=从表\u名称中选择MaxDateTake 其中StudentID=1且WritingSection不为NULL


如果要阅读Section和MathSection,您需要再运行两次此命令。

这很棘手。每个部分的分数可能来自不同的记录。但是max和min的正常规则不适用

以下查询获取每个节的序列号,从最新的非空值开始。然后用于外部查询中的条件聚合:

select s.StudentId,
       max(case when ws_seqnum = 1 then WritingSection end) as WritingSection,
       max(case when rs_seqnum = 1 then ReadingSection end) as ReadingSection,
       max(case when ms_seqnum = 1 then MathSection end) as MathSection,
       max(DateTaken) as DateTaken
from (select s.*,
             row_number() over (partition by studentid
                                order by (case when WritingSection is not null then 0 else 1 end), DateTaken desc
                               ) as ws_seqnum,
             row_number() over (partition by studentid
                                order by (case when ReadingSection is not null then 0 else 1 end), DateTaken desc
                               ) as rs_seqnum,
             row_number() over (partition by studentid
                                order by (case when MathSection is not null then 0 else 1 end), DateTaken desc
                               ) as ms_seqnum
      from student s
     ) s
where StudentId = 1
group by StudentId;
where子句在此查询中是可选的。您可以删除它,但它仍应适用于所有学生


此查询比需要的更复杂,因为数据未规范化。如果您对数据结构有控制,考虑一个关联/连接表,每个测试的每个学生一行,分数和测试日期作为表中的列。完全正常将引入另一个测试日期表,但这可能不是必需的。

Joe的解决方案将只返回一个学生id-最新参加测试的学生id。获取每个学生id的最新日期的方法是使用分析函数。下面是一个使用Oracle数据库的示例:

SELECT a.StudentID, a.DateTaken
  FROM (  SELECT StudentID,
             DateTaken,
             ROW_NUMBER ()
                OVER (PARTITION BY StudentID ORDER BY DateTaken DESC)
                rn
        FROM pto.test
    ORDER BY DateTaken DESC) a
 WHERE a.rn = 1
请注意“行数”函数如何将1放在每个学生id的最新日期。在“外部选择”中,您只需过滤rn=1的记录。。。仅执行内部选择以查看其工作方式。
让我知道您正在使用哪种数据库为您提供解决方案。每个数据库都有自己的分析功能实现,但逻辑是相同的…

这是SQL中一个非常经典的恼人问题-没有超优雅的方法来实现它。以下是我找到的最好的:

SELECT s.*
FROM Students s
JOIN (
  SELECT StudentID, MAX(DateTaken) as MaxDateTaken
  FROM Students
  GROUP BY StudentID
) f ON s.StudentID = f.StudentID AND s.DateTaken = f.MaxDateTaken
在日期字段上连接并不是非常理想,这会在出现MAX或fast的情况下中断,具体取决于表的索引方式。如果您的int rowID在所有行中都是唯一的,则最好执行以下操作:

SELECT s.*
FROM Students s
JOIN (
  SELECT rowID
  FROM (
    SELECT StudentID, rowID, row_number() OVER (PARTITION BY StudentID ORDER BY DateTaken DESC) as rowNumber
    FROM Students
  ) x
  WHERE x.rowNumber = 1
) f ON s.rowID = f.rowID

抱歉-我之前的回答回答了一个与前面提出的问题不同的问题:它将返回最近一行的所有数据。问题是汇总所有行,分别获取每个主题的最新分数

但是我把它放在那里,因为我回答的问题是一个普通的问题,也许有人在这个问题上提出了这个问题:

现在回答实际问题:

我认为最干净的方法是使用PIVOT和UNPIVOT:

SELECT StudentID, [WritingSection], [ReadingSection], [MathSection], MAX(DateTaken) DateTaken
FROM (
  SELECT StudentID, Subject, DateTaken, Score
  FROM (
    SELECT StudentID, Subject, DateTaken, Score
      , row_number() OVER (PARTITION BY StudentID, Subject ORDER BY DateTaken DESC) as rowNum
    FROM Students s
    UNPIVOT (
      Score FOR Subject IN ([WritingSection],[ReadingSection],[MathSection])
    ) u
  ) x
  WHERE x.rowNum = 1
) y
PIVOT (
  MAX(Score) FOR Subject IN ([WritingSection],[ReadingSection],[MathSection])
) p
GROUP BY StudentID, [WritingSection], [ReadingSection], [MathSection]
最里面的子查询x使用SQL的UNPIVOT函数来规范化数据含义,将每个学生在测试的每个部分上的分数转换为一行

下一个子查询out y只是将行分别过滤到每个主题的最新分数。这是SQL错误的一个解决方法,您不能在WHERE子句中使用像row_number这样的窗口函数

最后,由于您希望在测试的每个部分都以非规范化的原始格式1列显示数据,因此我们使用SQL的PIVOT函数。这只是将行转换为列—测试的每个部分对应一行。最后,您说您希望显示最近进行的测试,尽管每个部分都可能有自己独特的最新日期。因此,我们只需对这3个可能不同的日期记录进行汇总,就可以找到最新的日期


如果将来添加更多的节,这将比其他解决方案更容易扩展-只需将列名添加到列表中。

最近的分数还是最佳分数?例如,基于以上,我假设你想要学生1,98,86,38???最近的分数。我将编辑并放入预期结果。这是我担心我可能需要做的+1.提到正常化。不幸的是,我无法控制表结构。这个设计比你想象的更让人恼火——为了防止眼睛流血,我简化了这个问题:p这个看起来很有趣。我将阅读PIVOT和UNPIVOT:确实,必须这样做是一个代码sm
SELECT student.studentid, 
       WRITE.writingsection, 
       READ.readingsection, 
       math.mathsection, 
       student.datetaken 
FROM 
-- list of students / max dates taken 
(SELECT studentid, 
        Max(datetaken) datetaken 
 FROM   test_record 
 GROUP  BY studentid) student, 
-- greatest date for student with a writingsection score (dont care what the date is here, just that the score comes from the greatest date) 
(SELECT studentid, 
        writingsection 
 FROM   test_record t 
 WHERE  writingsection IS NOT NULL 
        AND datetaken = (SELECT Max(datetaken) 
                         FROM   test_record 
                         WHERE  studentid = t.studentid 
                                AND writingsection IS NOT NULL)) WRITE, 
(SELECT studentid, 
        readingsection 
 FROM   test_record t 
 WHERE  readingsection IS NOT NULL 
        AND datetaken = (SELECT Max(datetaken) 
                         FROM   test_record 
                         WHERE  studentid = t.studentid 
                                AND readingsection IS NOT NULL)) READ, 
(SELECT studentid, 
        mathsection 
 FROM   test_record t 
 WHERE  mathsection IS NOT NULL 
        AND datetaken = (SELECT Max(datetaken) 
                         FROM   test_record 
                         WHERE  studentid = t.studentid 
                                AND mathsection IS NOT NULL)) math 
WHERE 
  -- outer join in case a student has no score recorded for one or more of the sections  
  student.studentid = READ.studentid(+) 
  AND student.studentid = WRITE.studentid(+) 
  AND student.studentid = math.studentid(+); 
ell表示原始表没有正确规范化。这似乎是在无法修复表设计时临时引入规范化的一种好方法。
SELECT StudentID, [WritingSection], [ReadingSection], [MathSection], MAX(DateTaken) DateTaken
FROM (
  SELECT StudentID, Subject, DateTaken, Score
  FROM (
    SELECT StudentID, Subject, DateTaken, Score
      , row_number() OVER (PARTITION BY StudentID, Subject ORDER BY DateTaken DESC) as rowNum
    FROM Students s
    UNPIVOT (
      Score FOR Subject IN ([WritingSection],[ReadingSection],[MathSection])
    ) u
  ) x
  WHERE x.rowNum = 1
) y
PIVOT (
  MAX(Score) FOR Subject IN ([WritingSection],[ReadingSection],[MathSection])
) p
GROUP BY StudentID, [WritingSection], [ReadingSection], [MathSection]
SELECT student.studentid, 
       WRITE.writingsection, 
       READ.readingsection, 
       math.mathsection, 
       student.datetaken 
FROM 
-- list of students / max dates taken 
(SELECT studentid, 
        Max(datetaken) datetaken 
 FROM   test_record 
 GROUP  BY studentid) student, 
-- greatest date for student with a writingsection score (dont care what the date is here, just that the score comes from the greatest date) 
(SELECT studentid, 
        writingsection 
 FROM   test_record t 
 WHERE  writingsection IS NOT NULL 
        AND datetaken = (SELECT Max(datetaken) 
                         FROM   test_record 
                         WHERE  studentid = t.studentid 
                                AND writingsection IS NOT NULL)) WRITE, 
(SELECT studentid, 
        readingsection 
 FROM   test_record t 
 WHERE  readingsection IS NOT NULL 
        AND datetaken = (SELECT Max(datetaken) 
                         FROM   test_record 
                         WHERE  studentid = t.studentid 
                                AND readingsection IS NOT NULL)) READ, 
(SELECT studentid, 
        mathsection 
 FROM   test_record t 
 WHERE  mathsection IS NOT NULL 
        AND datetaken = (SELECT Max(datetaken) 
                         FROM   test_record 
                         WHERE  studentid = t.studentid 
                                AND mathsection IS NOT NULL)) math 
WHERE 
  -- outer join in case a student has no score recorded for one or more of the sections  
  student.studentid = READ.studentid(+) 
  AND student.studentid = WRITE.studentid(+) 
  AND student.studentid = math.studentid(+);