我想知道是否有人对相关子查询这个烦人的方面有解决方案:
如果我想让相关子查询返回多列......并且......根据主表中的值过滤子查询怎么办?!
例如。某天的学生考试成绩列表:
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;
并返回:
+-------+----------+-------------+
| name | topScore | topScoreDay |
+-------+----------+-------------+
| steve | 100 | 5 |
| joe | 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,这两个数据库似乎都没有一种可接受的方法来做到这一点,而不需要对每列进行令人讨厌的重复。当我发布这篇文章时,我看到 MS SQLServer 有一个
outer apply
连接运算符 - 一些东西就像 left join
但该子句可以引用 from 子句中的先前表 - 我想这就是我想要 Sqlite 拥有的?)
可以通过计算
row_number()
上的 partition by studentId order by score desc
,然后在行号 = 1 时对其执行 left join
来解决此特定查询。为了清楚起见,我已将该部分提取到 CTE 中,但您可以直接将其放入如果需要,在 left join
内:
with testScoresWithRowNumber as (
select
studentId,
score,
day,
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 testScoresWithRowNumber r on r.studentId = s.id and r.rn = 1
order by r.score desc;
输出:
name,topScore,topScoreDay
steve,100,5
joe,95,15