1

私の宿題の課題の 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

平均は正しくても、名前は正しくありません! Julia は csc で平均 100 を持っているのに、なぜ Michael が現れるのですか?


次に例を示します。

学生はコースを受講し、これらのコースの成績を取得します。例えば:

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

これでうまくいくはずです(そして、MySQLが実装する方法ではなく、SQL標準に従ってGROUP BYを使用します)

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

これら 2 つの結果は、学生テーブルに結合されます。読みやすくするために、average_grades(最初のステートメント) とmax_average_grades(2 番目のステートメント) というビューがあるとします。

最後のステートメントは、基本的にこれを行います。

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_gradesを置き換え、max_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 の 1 つであるため、最初のステートメントに固執する必要があります。

必要な派生テーブルが少ない標準 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

多くの異なる解決策があります。多分これは理解するのが簡単です:

/* 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 に答える
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 に答える