Sqlite多列关联子查询:有更好的解决方案吗?

问题描述 投票:0回答:1

我想知道是否有人对相关子查询这个烦人的方面有解决方案:

如果我想让相关子查询返回多列......并且......根据主表中的值过滤子查询怎么办?!

例如。某天的学生考试成绩列表:

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 拥有的?)

sqlite correlated-subquery
1个回答
0
投票

可以通过计算

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
© www.soinside.com 2019 - 2024. All rights reserved.