Одна частина мого домашнього завдання полягає в тому, щоб знайти студента з найвищим середнім значенням з кожного відділу.
QUERY:
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
RESULT:
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
RESULT:
csc Michael 100.0000
ger Robert 80.0000
sta Julie 77.0000
Хоча середні значення правильні, імена не є! Джулія - це той, хто має в середньому 100 в CSC, то чому Майкл з'являється?
Ось приклад:
студент проходить курси та отримує оцінки для цих курсів. EG:
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
Відповіді:
3 для відповіді № 1Це має зробити це (і він використовує GROUP BY відповідно до стандарту SQL, а не тим, як 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_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 - це одна з небагатьох СУБД, які не підтримують це, тому вам доведеться дотримуватися початкового твердження.
Стандартний 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;
0 для відповіді № 2
Оновити запит, щоб використовувати 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
0 для відповіді № 3
Є багато різних рішень. Можливо, цей простіше зрозуміти:
/* 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;