1

我的家庭作业的一部分是找到每个部门平均分最高的学生。

询问:

SELECT g.sid as studentID, s.sfirstname, s.dcode, AVG(grade) as average
FROM studentgrades g, student s
WHERE g.sid = s.sid
GROUP BY s.sid

结果:

1   Robert  ger 80.0000
2   Julie   sta 77.0000
3   Michael csc 84.0000
4   Julia   csc 100.0000
5   Patric  csc 86.0000
6   Jill    sta 74.5000

为了回答这个问题,我运行了查询

SELECT dcode, averages.sfirstName, MAX(averages.average)
FROM (
    SELECT g.sid as studentID, s.sfirstname, s.dcode, AVG(grade) as average
    FROM studentgrades g, student s
    WHERE g.sid = s.sid
    GROUP BY s.sid) averages
GROUP BY dcode

结果:

csc Michael 100.0000
ger Robert  80.0000
sta Julie   77.0000

即使平均值是正确的,名称也不正确!朱莉娅是 csc 平均 100 分的人,那么迈克尔为什么会出现呢?


这是一个例子:

学生参加课程并获得这些课程的成绩。例如:

student1 from dept1 took course A and got grade 80
student1 from dept1 took course B and got grade 90
student2 from dept1 took course C and got grade 100
student3 from dept2 took course X and got grade 90

运行第一个查询后,我们得到每个学生的平均值

student 1 from dept1 has average 85
student 2 from dept1 has average 100
student 3 from dept2 has average 90

现在我们找到每个部门平均分最高的学生

dept1, student2, 100
dept2, student3, 90
4

3 回答 3

3

应该这样做(并且它根据 SQL 标准使用 GROUP BY,而不是 MySQL 实现它的方式)

select s.sid,
       s.sfirstname,
       s.dcode,
       ag.avg_grade
from students s
  join (select sid, avg(grade) as avg_grade
        from studentgrades 
        group by sid) ag on ag.sid = s.sid
  join (select s.dcode,
               max(avg_grade) max_avg_grade
        from students s 
          join (select sid, avg(grade) as avg_grade
                from studentgrades 
                group by sid) ag on ag.sid = s.sid
        group by s.dcode) mag on mag.dcode = s.dcode and mag.max_avg_grade = ag.avg_grade
order by mag.avg_grade;

这是如何工作的

这通过几个步骤建立了结果。首先,它计算每个学生的平均成绩:

select sid, avg(grade) as avg_grade
from studentgrades 
group by sid

根据这个语句的结果,我们可以计算出最大值。平均水平:

select s.dcode,
       max(avg_grade) max_avg_grade
from students s 
  join (select sid, avg(grade) as avg_grade
        from studentgrades 
        group by sid) ag on ag.sid = s.sid
group by s.dcode

现在这两个结果被加入到学生表中。为了便于阅读,假设有一个名为average_grades(第一个语句)和max_average_grades(第二个语句)的视图。

最后的语句基本上是这样做的:

select s.sid,
       s.sfirstname,
       s.dcode,
       ag.avg_grade
from students s
  join avg_grades ag on ag.sid = s.sid
  join max_avg_grades mag 
    on mag.dcode = s.dcode 
   and mag.max_avg_grade = ag.avg_grade;

真正的(我回答中的第一个)只是简单地替换了名称avg_gradesmax_avg_grades我显示的选择。这就是为什么它看起来如此复杂。

标准 SQL 中的解决方案,更具可读性

在标准 SQL 中,这可以使用通用表表达式来表达,这使得它更具可读性(但本质上是相同的)

with avg_grades (sid, avg_grade) as (
  select sid, avg(grade) as avg_grade
  from studentgrades 
  group by sid
), 
max_avg_grades (dcode, max_avg_grade) as (
  select s.dcode, max(avg_grade) max_avg_grade
  from students s 
     join avg_grades ag on ag.sid = s.sid
  group by s.dcode
)
select s.sid,
       s.sfirstname,
       s.dcode,
       ag.avg_grade
from students s
  join avg_grades ag on ag.sid = s.sid
  join max_avg_grades mag on mag.dcode = s.dcode and mag.max_avg_grade = ag.avg_grade;

但是 MySQL 是极少数不支持此功能的 DBMS 之一,因此您需要坚持最初的声明。

需要较少派生表的标准 SQL 解决方案

在标准 SQL 中,使用窗口函数来计算部门内的排名甚至可以写得更短一些(同样这在 MySQL 中不起作用)

with avg_grades (sid, avg_grade) as (
  select sid, avg(grade) as avg_grade
  from studentgrades 
  group by sid
)
select sid, 
       sfirstname,
       dcode,
       avg_grade
from (       
  select s.sid,
         s.sfirstname,
         s.dcode,
         ag.avg_grade,
         rank() over (partition by s.dcode order by ag.avg_grade desc) as rnk
  from students s
    join avg_grades ag on ag.sid = s.sid
) t
where rnk = 1;
于 2012-11-04T08:28:00.360 回答
0

更新查询以使用HAVING如下子句:

   SELECT dcode, averages.sfirstName, averages.average
   FROM (
       SELECT g.sid as studentID, s.sfirstname, s.dcode, AVG(grade) as average
       FROM studentgrades g, student s
       WHERE g.sid = s.sid
       GROUP BY s.sid) averages
  GROUP BY dcode
  HAVING MAX(averages.average) = averages.average
于 2012-11-04T08:11:46.517 回答
0

有许多不同的解决方案。也许这个更容易理解:

/* create a new temporariy table of student performance. to keep the code clean and the performance better */
INSERT INTO studentperformance (studentID, sfirstname, dcode, average)
SELECT g.sid as studentID
     , s.sfirstname
     , s.dcode
     , AVG(grade) as average
FROM studentgrades g, student s
WHERE g.sid = s.sid
GROUP BY s.sid;

/* best grades for each department */
INSERT INTO bestgrades (best_average_per_department)
SELECT (dcode + '|' + MAX(average)) as best_average_per_department /* important string. maybe one has to cast the max(average) to string for this to work */
FROM studentperformance
GROUP BY dcode; /* important groub by ! */

/* get all the students who are best in each department */
SELECT a.studentID
     , a.sfirstname
     , a.dcode
     , a.average
FROM studentperformance as a
JOIN bestgrades as b on (a.dcode + '|' + a.average) = b.best_average_per_department; 
于 2012-11-04T10:33:17.587 回答