我有一个包含以下表格的数据库:
student(sid, name,address)
course(cid,type,department)
takes(sid,cid,score)
我需要解决的问题是找到学生,这样,对于他们所修的每门课程,他们在课程上的分数都高于任何其他修过同一门课程的学生。
我知道在这种情况下我必须进行自我加入,但仍然对如何解决此类查询感到困惑!
SELECT
s.sid,
s.name
FROM student s
WHERE NOT EXISTS (
SELECT *
FROM takes t1
JOIN takes t2 ON (t1.cid = t2.cid AND t1.sid <> t2.sid AND t2.score > t1.score)
WHERE t1.sid = s.sid
);
我们正在选择所有学生,我们找不到他们选修的任何其他人得分更高的课程。
试试这个:
SELECT s.*
FROM student s
INNER JOIN (
SELECT t1.*
FROM takes t1
LEFT JOIN takes t2
ON (t1.cid = t2.cid AND t1.score < t2.score)
WHERE t2.cid IS NULL
) b ON b.sid = s.sid
您可以在 fiddle 中看到,如果两个用户的分数相同即为最佳,则两者都将被重新使用。
你在表中找到得分最高的行takes
,然后加入Student
它
(我开始使用这种方式根据 Bill karwin 的答案从表格中获取最大值。您可以查看他对该答案的详细解释,了解其工作原理)。
SQB的回答完美解决了你的问题。我只是给你另一种方法。根据表的大小和可用的索引,它可以对性能产生很大影响。
我选择每门课程的最高分数,并加入所有课程。然后我将学生参加的所有课程的数量与他/她获得最高分的课程数量进行比较。因此,表格被完整地阅读了两次,结果以某种方式粘合在一起然后过滤。这可以(但不是必须)比必须在需要中找到的每条记录搜索更高等级的学生更快。
select *
from students
where sid in
(
select takes.sid
from takes
left outer join (select cid, max(score) as score from takes group by cid) max_scores
on (max_scores.cid=takes.cid and max_scores.score=takes.score)
group by takes.sid
having count(takes.cid) = count(max_scores.cid)
);
如果要求是“对于每门课程,找到获得最高分的学生”,那么可以使用以下查询来完成:
WITH ranked_scores AS (
SELECT sid,
cid,
score,
DENSE_RANK() OVER ( PARTITION BY cid ORDER BY score DESC ) AS "rank"
FROM takes
)
SELECT s.sid, s.name, r.cid, r.score
FROM student s
INNER JOIN
ranked_scores r
ON ( r.sid = s.sid )
WHERE r."rank" = 1;
如果要求是“找到在所有课程中获得该课程最高分的学生”,那么你可以使用这个:
WITH ranked_scores AS (
SELECT sid,
cid,
score,
DENSE_RANK() OVER ( PARTITION BY cid ORDER BY score DESC ) AS "rank"
FROM takes
)
SELECT s.sid,
s.name
FROM student s
WHERE EXISTS ( SELECT 1
FROM ranked_scores r
WHERE r.sid = s.sid
GROUP BY r.sid
HAVING MAX( r."rank" ) = 1 );