gpt4 book ai didi

mysql - 构造二维 MySQL 查询的最有效方法是什么?

转载 作者:可可西里 更新时间:2023-11-01 07:35:30 24 4
gpt4 key购买 nike

我有一个包含以下表和字段的 MySQL 数据库:

  • 学生 (id)
  • 类(id)
  • 年级(id、student_id、class_id、年级)

学生表和类(class)表在 id(主键)上建立索引。成绩表以 id(主键)和 student_id、class_id 和成绩为索引。

我需要构建一个查询,在给定类(class) ID 的情况下,给出所有其他类(class)的列表以及在其他类(class)中得分更高的学生人数。

基本上,给定成绩表中的以下数据:

student_id | class_id | grade
--------------------------------------
1 | 1 | 87
1 | 2 | 91
1 | 3 | 75
2 | 1 | 68
2 | 2 | 95
2 | 3 | 84
3 | 1 | 76
3 | 2 | 88
3 | 3 | 71

用类 ID 1 查询应该产生:

class_id | total
-------------------
2 | 3
3 | 1

理想情况下,我希望它能在几秒钟内执行,因为我希望它成为网络界面的一部分。

我遇到的问题是,在我的数据库中,我有超过 1300 个类(class)和 160,000 名学生。我的成绩表有将近 1500 万行,因此,查询需要很长时间才能执行。

这是我到目前为止尝试过的以及每个查询所用的时间:

-- I manually stopped execution after 2 hours
SELECT c.id, COUNT(*) AS total
FROM classes c
INNER JOIN grades a ON a.class_id = c.id
INNER JOIN grades b ON b.grade < a.grade AND
a.student_id = b.student_id AND
b.class_id = 1
WHERE c.id != 1 AND
GROUP BY c.id

-- I manually stopped execution after 20 minutes
SELECT c.id,
(
SELECT COUNT(*)
FROM grades g
WHERE g.class_id = c.id AND g.grade > (
SELECT grade
FROM grades
WHERE student_id = g.student_id AND
class_id = 1
)
) AS total
FROM classes c
WHERE c.id != 1;

-- 1 min 12 sec
CREATE TEMPORARY TABLE temp_blah (student_id INT(11) PRIMARY KEY, grade INT);
INSERT INTO temp_blah SELECT student_id, grade FROM grades WHERE class_id = 1;
SELECT o.id,
(
SELECT COUNT(*)
FROM grades g
INNER JOIN temp_blah t ON g.student_id = t.student_id
WHERE g.class_id = c.id AND t.grade < g.grade
) AS total
FROM classes c
WHERE c.id != 1;

-- Same thing but with joins instead of a subquery - 1 min 54 sec
SELECT c.id,
COUNT(*) AS total
FROM classes c
INNER JOIN grades g ON c.id = p.class_id
INNER JOIN temp_blah t ON g.student_id = t.student_id
WHERE c.id != 1
GROUP BY c.id;

我还考虑过创建一个二维表,以学生为行,以类(class)为列,但是我发现了两个问题:

  • MySQL 实现了最大列数 (4096) 和最大行大小(以字节为单位),此方法可能会超过此值
  • 我想不出查询该结构以获得我需要的结果的好方法

我还考虑过将这些计算作为后台作业执行并将结果存储在某处,但为了使信息保持最新(必须),每次创建或更新学生、类(class)或成绩记录时都需要重新计算它们。

有人知道构造此查询的更有效方法吗?

编辑:创建表语句:

CREATE TABLE `classes` (
`id` int(11) NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1331 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci$$

CREATE TABLE `students` (
`id` int(11) NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=160803 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci$$

CREATE TABLE `grades` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`student_id` int(11) DEFAULT NULL,
`class_id` int(11) DEFAULT NULL,
`grade` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `index_grades_on_student_id` (`student_id`),
KEY `index_grades_on_class_id` (`class_id`),
KEY `index_grades_on_grade` (`grade`)
) ENGINE=InnoDB AUTO_INCREMENT=15507698 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci$$

最有效查询(1 分 12 秒)的解释输出:

id | select_type        | table | type   | possible_keys                                                             | key                      | key_len | ref               | rows   | extra 
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
1 | PRIMARY | c | range | PRIMARY | PRIMARY | 4 | | 683 | Using where; Using index
2 | DEPENDENT SUBQUERY | g | ref | index_grades_on_student_id,index_grades_on_class_id,index_grades_on_grade | index_grades_on_class_id | 5 | mydb.c.id | 830393 | Using where
2 | DEPENDENT SUBQUERY | t | eq_ref | PRIMARY | PRIMARY | 4 | mydb.g.student_id | 1 | Using where

另一个编辑 - 解释 sgeddes 建议的输出:

+----+-------------+------------+--------+---------------+------+---------+------+----------+----------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+------------+--------+---------------+------+---------+------+----------+----------------------------------------------+
| 1 | PRIMARY | <derived2> | ALL | NULL | NULL | NULL | NULL | 14953992 | Using where; Using temporary; Using filesort |
| 2 | DERIVED | <derived3> | system | NULL | NULL | NULL | NULL | 1 | Using filesort |
| 2 | DERIVED | G | ALL | NULL | NULL | NULL | NULL | 15115388 | |
| 3 | DERIVED | NULL | NULL | NULL | NULL | NULL | NULL | NULL | No tables used |
+----+-------------+------------+--------+---------------+------+---------+------+----------+----------------------------------------------+

最佳答案

我认为这对使用 SUMCASE 的你应该有效:

SELECT C.Id,
SUM(
CASE
WHEN G.Grade > C2.Grade THEN 1 ELSE 0
END
)
FROM Class C
INNER JOIN Grade G ON C.Id = G.Class_Id
LEFT JOIN (
SELECT Grade, Student_Id, Class_Id
FROM Class
JOIN Grade ON Class.Id = Grade.Class_Id
WHERE Class.Id = 1
) C2 ON G.Student_Id = C2.Student_Id
WHERE C.Id <> 1
GROUP BY C.Id

Sample Fiddle Demo

--编辑--

针对您的评论,这是另一种应该更快的尝试:

SELECT 
Class_Id,
SUM(CASE WHEN Grade > minGrade THEN 1 ELSE 0 END)
FROM
(
SELECT
Student_Id,
@classToCheck:=
IF(G.Class_Id = 1, Grade, @classToCheck) minGrade ,
Class_Id,
Grade
FROM Grade G
JOIN (SELECT @classToCheck:= 0) t
ORDER BY Student_Id, IF(Class_Id = 1, 0, 1)
) t
WHERE Class_Id <> 1
GROUP BY Class_ID

more sample fiddle .

关于mysql - 构造二维 MySQL 查询的最有效方法是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15036871/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com