Как включить строку в запрос GROUP BY в MySQL?

У меня есть таблица для записи оценки, полученной учеником по предмету в конкретную дату. В таблице perf есть столбцы «id», «дата», «студент», «тема», «отметка».

Цель: я хочу сравнить оценку, полученную учеником, с самой высокой, самой низкой и средней оценкой класса по конкретному предмету за каждый день, используя один запрос, например:

SELECT date,mark,highest,lowest,avg,student FROM ....   

.... и получите такой результат:

дата отметка самый высокий самый низкий среднее студент 07.09.2021 73 82 58 69 2с3 09.09.2021 81 84 62 75 2с3 14 сентября 2021 г. 78 78 68 73 2с3 2021-09-17 75 89 59 73 2с3

Как мне достичь своей цели в одном заявлении?

Я открыт для промежуточных шагов, например, создания представлений.

Я использую MySQL 8.0.33.

Вот данные:

идентификатор дата студент предмет отметка 1 2021-09-6 а1б Английский 78 2 2021-09-7 а1б Искусство 63 3 8 сентября 2021 г. а1б Математика 67 4 09.09.2021 а1б Искусство 71 5 09.09.2021 а1б Английский 74 6 10.09.2021 а1б Математика 65 7 2021-09-13 а1б Английский 81 8 14 сентября 2021 г. а1б Искусство 68 9 2021-09-15 а1б Математика 70 10 16 сентября 2021 г. а1б Английский 79 11 2021-09-17 а1б Искусство 70 12 2021-09-17 а1б Математика 68 14 2021-09-6 2с3 Английский 68 15 07.09.2021 2с3 Искусство 73 16 8 сентября 2021 г. 2с3 Математика 57 17 09.09.2021 2с3 Искусство 81 18 09.09.2021 2с3 Английский 74 19 10.09.2021 2с3 Математика 55 20 2021-09-13 2с3 Английский 73 21 14 сентября 2021 г. 2с3 Искусство 78 22 2021-09-15 2с3 Математика 60 23 16 сентября 2021 г. 2с3 Английский 71 24 2021-09-17 2с3 Искусство 75 25 2021-09-17 2с3 Математика 58 26 2021-09-6 3d4 Английский 53 27 07.09.2021 3d4 Искусство 58 28 8 сентября 2021 г. 3d4 Математика 56 29 09.09.2021 3d4 Искусство 62 30 09.09.2021 3d4 Английский 54 31 10.09.2021 3d4 Математика 51 32 2021-09-13 3d4 Английский 51 33 14 сентября 2021 г. 3d4 Искусство 68 34 2021-09-15 3d4 Математика 60 35 16 сентября 2021 г. 3d4 Английский 58 36 2021-09-17 3d4 Искусство 59 37 2021-09-17 3d4 Математика 58 38 2021-09-6 4эф Английский 87 39 07.09.2021 4эф Искусство 82 40 8 сентября 2021 г. 4эф Математика 91 41 09.09.2021 4эф Искусство 84 42 09.09.2021 4эф Английский 79 43 10.09.2021 4эф Математика 81 44 2021-09-13 4эф Английский 73 45 14 сентября 2021 г. 4эф Искусство 78 46 2021-09-15 4эф Математика 82 47 16 сентября 2021 г. 4эф Английский 82 48 2021-09-17 4эф Искусство 89 49 2021-09-17 4эф Математика 92

Я могу получить высшую, низшую и среднюю оценку класса, используя GROUP BY следующим образом:

SELECT date,subject,MAX(mark) AS highest, MIN(mark) AS lowest, FORMAT(AVG(mark),0) AS avg FROM perf GROUP BY date,subject   
дата отметка самый высокий самый низкий среднее 2021-09-06 Английский 87 53 72 07.09.2021 Искусство 82 58 69 2021-09-08 Математика 91 56 68 09.09.2021 Искусство 84 62 75 09.09.2021 Английский 79 54 70 10.09.2021 Математика 81 51 63

Попытка включить оценку и ученика в приведенный выше запрос дает бессмысленные результаты:

SELECT date,subject,mark,MAX(mark) AS highest, MIN(mark) AS lowest, FORMAT(AVG(mark),0) AS avg,student FROM perf GROUP BY date,subject,mark,student  
дата предмет отметка самый высокий самый низкий среднее студент 16 сентября 2021 г. Английский 71 71 71 71 2с3 07.09.2021 Искусство 73 73 73 73 2с3 2021-09-13 Английский 73 73 73 73 2с3 2021-09-13 Английский 73 73 73 73 4эф 09.09.2021 Английский 74 74 74 74 а1б 09.09.2021 Английский 74 74 74 74 2с3 2021-09-17 Искусство 75 75 75 75 2с3 2021-09-06 Английский 78 78 78 78 а1б

Я пробовал несколько вещей, но мне удалось объединить оценку, высшую, самую низкую, среднюю, студентку в один результат, когда я указываю студента, предмет и дату следующим образом, но это слишком специфично и не то, что я хочу:

SELECT (SELECT date FROM perf WHERE student='2c3' AND subject='Art' AND date='2021-09-07') AS date,  
(SELECT mark FROM perf WHERE student='2c3' AND subject='Art' AND date='2021-09-07') AS mark,  
(SELECT MAX(mark) FROM perf WHERE subject='Art' AND date='2021-09-07') AS highest,  
(SELECT MIN(mark) FROM perf WHERE subject='Art' AND date='2021-09-07') AS lowest,  
(SELECT FORMAT(AVG(mark),0) FROM perf WHERE subject='Art' AND date='2021-09-07') AS avg 
дата отметка самый высокий самый низкий среднее 07.09.2021 73 82 58 69

🤔 А знаете ли вы, что...
MySQL предоставляет средства для оптимизации запросов с использованием индексов и профилирования.


65
4

Ответы:

Доказательство работы Ввод и вывод GitHub из компилятора SQL Online: https://gist.github.com/xentzenith/ccdcc3e11f377195645cf2db8a4dccf0

Для достижения цели сравнения оценки, полученной учащимся, с самой высокой, самой низкой и средней оценкой класса по конкретному предмету за каждый день, вы можете использовать комбинацию оконных функций и подзапросов.

WITH stats AS (
    SELECT
        date,
        subject,
        MAX(mark) OVER (PARTITION BY date, subject) AS highest,
        MIN(mark) OVER (PARTITION BY date, subject) AS lowest,
        AVG(mark) OVER (PARTITION BY date, subject) AS avg
    FROM perf
)
SELECT
    p.date,
    p.mark,
    s.highest,
    s.lowest,
    ROUND(s.avg, 0) AS avg,
    p.student
FROM perf p
JOIN stats s ON p.date = s.date AND p.subject = s.subject
WHERE p.mark = s.mark;

CTE статистики вычисляет самые высокие, самые низкие и средние оценки для каждого предмета на каждую дату, используя оконные функции (MAX(), MIN(), AVG()). Предложение PARTITION BY указывает группу строк, к которым применяется агрегатная функция. Это должно работать для вас, если вы используете соответствующую версию SQL :)


Используйте CTE, чтобы вычислить общие значения и присоединиться к ним:

with cte as (
  SELECT
    date,
    subject,
    MAX(mark) AS highest,
    MIN(mark) AS lowest,
    FORMAT(AVG(c.mark), 0) AS avg
  FROM perf
  GROUP BY date, subject  
)
SELECT
    c.date,
    c.subject,
    p.mark,
    c.highest,
    c.lowest,
    c.avg,
    p.student
FROM perf p
JOIN cte c ON c.date = p.date
  AND c.subject = p.subject

Возможно, вы можете использовать оконную функцию mysql8, она может собирать данные из нескольких измерений в одной строке.

SELECT 
    p.*,
    MAX(p.mark) over(PARTITION BY p.date, p.subject) AS highest,
    MIN(p.mark) over(PARTITION BY p.date, p.subject) AS lowest,
    format(AVG(p.mark) over(PARTITION BY p.date, p.subject), 0) AS AVG  
FROM perf p 

результат:

идентификатор дата студент предмет отметка самый высокий самый низкий AVG 1 2021-09-06 а1б Английский 78 87 53 72 14 2021-09-06 2с3 Английский 68 87 53 72 38 2021-09-06 4эф Английский 87 87 53 72 26 2021-09-06 3d4 Английский 53 87 53 72 2 07.09.2021 а1б Искусство 63 82 58 69 ...

Это простое использование оконных функций. может помочь тебе.


Решено
SELECT
a.DATE,
b.lowest,
b.highest,
b.avg,
a.student,
a.mark
FROM perf a
LEFT JOIN (
SELECT
DATE,
SUBJECT, 
MIN(mark) AS lowest,
MAX(mark) AS highest,
AVG(mark) AS AVG
FROM perf
GROUP BY DATE,SUBJECT ) b ON a.date = b.date AND a.subject = b.subject