假设我有几个表格,用于存储学生的测试标记,就像这样
tests (
testId INT,
proctoredDate DATE,
totalMark INT,
weight FLOAT,
CONSTRAINT test_id_primary_key PRIMARY KEY(testId),
...
)
student_test_marks (
studentId INT,
testId INT,
awardedMark INT,
CONSTRAINT student_test_composite_key PRIMARY KEY(studentId, testId),
...
)
我正在尝试确定给定月份中哪个学生的累积分数最高。因此,该过程是对每个学生在指定月份中分组的所有测试成绩和权重求和,并仅返回最大和。我已经有一个有效的查询,但我认为它不是非常有效。
SELECT studentId
FROM student_test_marks INNER JOIN tests
ON student_test_marks.testId = tests.testId
GROUP BY studentId, EXTRACT(MONTH FROM proctoredDate)
HAVING EXTRACT(MONTH FROM proctoredDate) = 4 AND
SUM((awardedMark / totalMark) * weight) IN (
SELECT MAX(SUM((awardedMark / totalMark) * weight))
FROM student_test_marks INNER JOIN tests
ON student_test_marks.testId = test.testId
GROUP BY studentId, EXTRACT(MONTH FROM proctoredDate)
HAVING EXTRACT(MONTH FROM proctoredDate) = 4
)
它确实可以工作,但是我感觉它不是最佳的,因为查询将联接,分组和过滤两次。我考虑过按组的“求和”列排序,然后仅获取第一行,但感觉很笨拙。
是否有更好的方法来解决这个问题?
答案 0 :(得分:1)
我认为您只想使用分析功能:
SELECT m.*
FROM (SELECT TRUNC(proctoredDate, 'MON') as yyyymm,
studentId, SUM((awardedMark / totalMark) * weight) as weighted_mark,
RANK() OVER (PARTITION BY TRUNC(proctoredDate, 'MON')
ORDER BY SUM((awardedMark / totalMark) * weight)
) as seqnum
FROM student_test_marks stm INNER JOIN
tests t
ON stm.testId = t.testId
GROUP BY TRUNC(proctoredDate, 'MON'), studentId
) m
WHERE seqnum = 1;
答案 1 :(得分:0)
with tests as (
select 1 testid, date '2019-01-10' proctoredDate, 100 totalmark from dual union all
select 2 testid, date '2019-02-22' proctoredDate, 100 totalmark from dual union all
select 3 testid, date '2019-03-14' proctoredDate, 100 totalmark from dual union all
select 4 testid, date '2019-04-19' proctoredDate, 100 totalmark from dual),
student_test_marks as (
select 1 studentid, 1 testid, 50 awardedmark from dual union all
select 2 studentid, 1 testid, 30 awardedmark from dual union all
select 3 studentid, 1 testid, 70 awardedmark from dual union all
select 4 studentid, 1 testid, 60 awardedmark from dual union all
select 1 studentid, 2 testid, 30 awardedmark from dual union all
select 2 studentid, 2 testid, 40 awardedmark from dual union all
select 3 studentid, 2 testid, 50 awardedmark from dual union all
select 4 studentid, 2 testid, 70 awardedmark from dual union all
select 1 studentid, 3 testid, 70 awardedmark from dual union all
select 2 studentid, 3 testid, 60 awardedmark from dual union all
select 2 studentid, 4 testid, 30 awardedmark from dual union all
select 1 studentid, 4 testid, 40 awardedmark from dual union all
select 2 studentid, 4 testid, 50 awardedmark from dual
)
select
mn,
max(studentid) keep (dense_rank last order by sumawarded) studentid,
max(sumawarded) max_sumawarded
from
(select trunc(t.proctoredDate, 'month') mn, s.studentid, sum(awardedmark) sumawarded
from tests t
inner join student_test_marks s on t.testId = s.testId
group by trunc(t.proctoredDate, 'month'), s.studentid
)
group by mn;
MN STUDENTID MAX_SUMAWARDED
------------------- ---------- --------------
2019-01-01 00:00:00 3 70
2019-02-01 00:00:00 4 70
2019-03-01 00:00:00 1 70
2019-04-01 00:00:00 2 80