MySQL 查看聚合查询的性能问题

MySQL View performance issue with aggregate query

我正在使用 mysql 版本 5.6.47。 我有以下 table 学生成绩:

CREATE TABLE `studentmarks` (
  `ID` int(11) NOT NULL AUTO_INCREMENT,
  `StudentID` int(11) NOT NULL,
  `subjectName` varchar(255) DEFAULT NULL,
  `MARKS` int(11) NOT NULL,
  PRIMARY KEY (`ID`),
  KEY `idx_studentmarks_StudentID` (`StudentID`)
);

并在 table 上创建了一个视图:

CREATE OR REPLACE VIEW `vw_student_marks` AS
    SELECT 
        `s1`.`StudentID` AS `StudentID`,
        `s1`.`subjectName` AS `subjectName`,
        `s1`.`MARKS` AS `marks`,
        (SELECT 
                SUM(`s2`.`MARKS`)
            FROM
                `studentmarks` `s2`
            WHERE
                (`s2`.`StudentID` = `s1`.`StudentID`)) AS `totalMarks`
    FROM
        `studentmarks` `s1`;

当测试大约 20K 行时,运行 SELECT querySELECT * FROM VIEW 的性能存在明显差异。 select 查询显示优化的执行计划,只有 1 次完整 table 扫描,而视图有 2 次完整 table 扫描。

查询统计数据(由 MySQL Workbench 测量):

SELECT 查询

Timing: 0:00:0.07677120 (as measured by the server)

Rows Examined: 108285

SELECT 来自查看查询:

Timing: 0:00:1.6082441 (as measured by the server)

Rows Examined: 2985730

这种性能差异背后的原因是什么?

查询执行计划:https://i.stack.imgur.com/noOxI.jpg

更新:我用 MySQL 版本 8.0.19 进行了测试,出现了同样的问题

在这种情况下,

MySQL 必须对视图使用 TEMPTABLE 算法(聚合函数)。这可能是造成差异的原因。

详情请参考https://dev.mysql.com/doc/refman/5.6/en/view-algorithms.html

If the MERGE algorithm cannot be used, a temporary table must be used instead. MERGE cannot be used if the view contains any of the following constructs:

Aggregate functions (SUM(), MIN(), MAX(), COUNT(), and so forth)

DISTINCT

GROUP BY

HAVING

LIMIT

UNION or UNION ALL

Subquery in the select list

Assignment to user variables

Refers only to literal values (in this case, there is no underlying table)

FWIW,我会使用不相关的子查询来编写此代码(但我承认它不一定会提高性能)- 并放弃完全使用视图的想法...

SELECT s1.StudentID
     , s1.subjectName
     , s1.MARKS
     , s2.totalmarks
  FROM studentmarks s1
  JOIN
     ( SELECT studentid
            , SUM(s2.MARKS) totalmarks
         FROM studentmarks
        GROUP 
           BY studentid
     ) s2
    ON s2.studentid = s1.studentid;