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(+);