我希望相关子查询返回多列并且根据主表中的值过滤子查询。
某天的学生考试成绩列表:
create table student
(id, name ); insert into student values
(1 , 'joe' ),
(2 , 'steve' );
create table testScore
(id, studentId, day , score); insert into testScore values
(1 , 1 , 5 , 70 ),
(2 , 1 , 10 , 68 ),
(3 , 1 , 15 , 95 ),
(4 , 1 , 20 , 81 ),
(5 , 2 , 5 , 100 ),
(6 , 2 , 10 , 75 ),
(7 , 2 , 15 , 98 ),
(8 , 2 , 20 , 92 );
create index testScore_studentTopScores on testScore (studentId, score, day);
如果我需要为每个学生获得最高的考试成绩(和包括相应的天数,和有使用的
testScore_studentTopScores
索引)我很乐意这样做:
select
student.name,
topScore.score as topScore,
topScore.day as topScoreDay
from
student
left join (
select * from
testScore
where
testScore.studentId = student.id
order by
score desc
limit 1
) as topScore
order by
topScore desc;
并让它返回:
名字 | 最高分 | 最高得分日 |
---|---|---|
史蒂夫 | 100 | 5 |
乔 | 95 | 15 |
但是
from
子句子查询不能引用外部/父表。列子查询和 where
子句子查询能够访问 from
子句中的表,但不能返回多列。所以我必须为每一列复制子查询:
select
student.name,
(
select
score
from
testScore
where
testScore.studentId = student.id
order by
score desc
limit 1
) as topScore,
(select day from (
select
score,
day
from
testScore
where
testScore.studentId = student.id
order by
score desc
limit 1
)) as topScoreDay
from
student
order by
topScore desc;
SQLite 和 MySQL 都无法在不重复每列的情况下做到这一点。 Microsoft SQL Server 有一个
outer apply
连接运算符;类似于 left join
,但该子句可以引用 from
子句中先前的表。这就是我想要的 SQLite。
可以通过计算
row_number()
上的 partition by studentId order by score desc
,然后在行号 = 1 时对其执行 left join
来解决此特定查询。为了清楚起见,我已将该部分提取到 CTE 中,但您可以直接将其放入如果需要,在 left join
内:
with testScoreWithRowNumber as (
select *, row_number() over (partition by studentId order by score desc) as rn
from testScore
)
select
s.name,
r.score as topScore,
r.day as topScoreDay
from student s
left join testScoreWithRowNumber r on r.studentId = s.id and r.rn = 1
order by r.score desc;
输出:
name,topScore,topScoreDay
steve,100,5
joe,95,15
您可以与学生的最高分左连接,并与更好的分数左连接,并过滤掉任何具有更好分数的元组:
select
student.name,
topScore.score as topScore,
topScore.day as topScoreDay
from
student
left join testScore as topScore
on topScore.studentId = student.id
left join testScore as evenBetterScore
on topScore.studentId = evenBetterScore.studentId and topScore.score < evenBetterScore.score
where evenBetterScore.studentId is null
order by
topScore.score desc;
因此,您最终将获得每个学生的单个记录,以及该学生的最高分(如果存在)。
我认为答案要简单得多;
SELECT s.name, MAX(t.score) as topScore, t.day AS topScoreDay
FROM student as s
LEFT JOIN testScore AS t ON s.id = t.studentId
GROUP BY s.id
ORDER BY topScore DESC, t.score ASC;
结果:
名字 | 最高分 | 最高得分日 |
---|---|---|
史蒂夫 | 100 | 5 |
乔 | 95 | 15 |
高效,每个学生只需查找一次;将我想要的列塞入一个 JSON 列并稍后提取它们:
select
topScore.name,
topScore.topScoreJson ->> 'score' as topScore,
topScore.topScoreJson ->> 'day' as topScoreDay
from (
select
student.name,
(
select
json_object(
'score', testScore.score,
'day', testScore.day
)
from
testScore
where
testScore.studentId = student.id
order by
testScore.score desc
limit 1
) as topScoreJson
from
student
) as topScore
order by
topScore desc;
根据 数据库管理员的回答 SQLite 仅在直接连接子查询时不允许访问外部表。它确实允许访问属于
on
子句一部分的子查询内的外部表。
使用“相关标量子查询”,它允许访问父
student
表的当前行。无需在子查询中使用 limit 1
,因为 SQLite 仅返回满足 on
子句的第一行。
select
student.name,
testScore.score as topScore,
testScore.day as topScoreDay
from
student
left join testScore on testScore.id = (
select id from
testScore
where
testScore.studentId = student.id
order by
score desc
)
order by
topScore desc;