MySQL - 如何改进使用多个LEFT JOIN的复杂查询

时间:2014-07-16 13:40:16

标签: mysql sql left-join

我有以下小提琴http://sqlfiddle.com/#!2/21cee/2

这是数据库表的架构:

平均值

id
subject_id
student_id
value
semester_id

classes_students

id
class_academic_year_id
student_id

classes_academic_years

id
class_id
name
academic_year_id
grade

classes_subjects

id
class_academic_year_id
subject_id

class_masters

id
class_academic_year_id
professor_id

教授

id
school_id
first_name
last_name

professors_subjects

id
professor_id
subject_id

professors_classes_subjects

id
professor_id
class_academic_year_id
subject_id

受试者

id
name
school_id
default

学期

id
name
academic_year_id
start_date
end_date

id
last_name
first_name

我收到了以下问题:

SELECT `averages`.* 
FROM `averages` 
INNER JOIN `classes_students` ON classes_students.student_id = averages.student_id 
INNER JOIN `classes_academic_years` ON classes_academic_years.id = classes_students.class_academic_year_id 
INNER JOIN `classes_subjects` ON classes_subjects.class_academic_year_id = classes_academic_years.id 
INNER JOIN `subjects` ON subjects.id = classes_subjects.subject_id 
INNER JOIN `professors_classes_subjects` ON professors_classes_subjects.class_academic_year_id = classes_subjects.class_academic_year_id 
INNER JOIN `professors_subjects` ON professors_subjects.professor_id = professors_classes_subjects.professor_id 
INNER JOIN `professors` ON professors.id = professors_subjects.professor_id 
INNER JOIN `semesters` ON semesters.academic_year_id = classes_academic_years.academic_year_id 
INNER JOIN `students` ON students.id = classes_students.student_id 

WHERE (classes_academic_years.academic_year_id = 3) 
AND (subjects.id = 72) 
AND (professors.id = 198) 
AND (professors_classes_subjects.subject_id = subjects.id) 
AND (professors_subjects.subject_id = subjects.id) 
AND (averages.subject_id = subjects.id) 
AND (averages.semester_id = semesters.id) 

ORDER BY `averages`.`student_id` asc, `averages`.`semester_id` asc;

SELECT `averages`.* 
FROM `averages` 
INNER JOIN `classes_students` ON classes_students.student_id = averages.student_id 
INNER JOIN `classes_academic_years` ON classes_academic_years.id = classes_students.class_academic_year_id 
INNER JOIN `classes_subjects` ON classes_subjects.class_academic_year_id = classes_academic_years.id 
INNER JOIN `subjects` ON subjects.id = classes_subjects.subject_id 
INNER JOIN `class_masters` ON class_masters.class_academic_year_id = classes_academic_years.id
INNER JOIN `semesters` ON semesters.academic_year_id = classes_academic_years.academic_year_id 
INNER JOIN `students` ON students.id = classes_students.student_id 

WHERE (classes_academic_years.academic_year_id = 3) 
AND (subjects.id = 72) 
AND (class_masters.professor_id = 198)
AND (averages.subject_id = subjects.id) 
AND (averages.semester_id = semesters.id) 

ORDER BY `averages`.`student_id` asc, `averages`.`semester_id` asc;

第一个查询获取教授所分配的所有课程的某个教授(subject_id = 72)的某个科目(professor_id = 198)的平均值。

由于professor_id = 198没有为任何类分配给subject_id = 72,因此结果将为空。

第二个查询获取某个教授(subject_id = 72)的某个主题(professor_id = 198)的平均值,仅检查给定教授是class_master的那些类,而不管哪个教授是分配给该课程的该科目。

两个查询都运行正常。我需要结合这两个查询,这是我的尝试:

SELECT DISTINCT `averages`.* FROM `averages` 
INNER JOIN `classes_students` ON classes_students.student_id = averages.student_id 
INNER JOIN `classes_academic_years` ON classes_academic_years.id = classes_students.class_academic_year_id 
INNER JOIN `classes_subjects` ON classes_subjects.class_academic_year_id = classes_academic_years.id 
INNER JOIN `subjects` ON subjects.id = classes_subjects.subject_id 
LEFT JOIN `professors_classes_subjects` ON professors_classes_subjects.class_academic_year_id = classes_subjects.class_academic_year_id 
LEFT JOIN `professors_subjects` ON professors_subjects.professor_id = professors_classes_subjects.professor_id 
LEFT JOIN `professors` ON professors.id = professors_subjects.professor_id 
LEFT JOIN `class_masters` ON class_masters.class_academic_year_id = classes_academic_years.id 
INNER JOIN `semesters` ON semesters.academic_year_id = classes_academic_years.academic_year_id 
INNER JOIN `students` ON students.id = classes_students.student_id 
WHERE (classes_academic_years.academic_year_id = 3) 
AND (subjects.id = 72) 
AND ((professors.id = 198 
AND professors_classes_subjects.subject_id = subjects.id 
AND professors_subjects.subject_id = subjects.id) 
OR (class_masters.id is not null AND class_masters.professor_id = 198)) 
AND (averages.subject_id = subjects.id) 
AND (averages.semester_id = semesters.id) 
ORDER BY `averages`.`student_id` asc, `averages`.`semester_id` asc;

虽然它确实工作正常,但它也非常慢(例如,它在我的本地计算机上运行大约需要5秒钟。)

有没有办法改进第三个查询?

LE:这里是EXPLAIN:

+----+-------------+-----------------------------+--------+---------------------------------------------------------------------+-----------------------------------+---------+-----------------------------------------------+------+----------------------------------------------------+
| id | select_type |            table            |  type  |                            possible_keys                            |                key                | key_len |                      ref                      | rows |                       Extra                        |
+----+-------------+-----------------------------+--------+---------------------------------------------------------------------+-----------------------------------+---------+-----------------------------------------------+------+----------------------------------------------------+
|  1 | SIMPLE      | subjects                    | const  | PRIMARY                                                             | PRIMARY                           | 8       | const                                         |    1 | Using index; Using temporary; Using filesort       |
|  1 | SIMPLE      | averages                    | ref    | subject_id_student_id_semester_id                                   | subject_id_student_id_semester_id | 8       | const                                         | 1087 | (NULL)                                             |
|  1 | SIMPLE      | students                    | eq_ref | PRIMARY                                                             | PRIMARY                           | 8       | averages.student_id                           |    1 | Using index                                        |
|  1 | SIMPLE      | classes_students            | ref    | student_id,class_academic_year_id_student_id                        | student_id                        | 8       | averages.student_id                           |    2 | (NULL)                                             |
|  1 | SIMPLE      | semesters                   | eq_ref | PRIMARY,academic_year_id                                            | PRIMARY                           | 4       | averages.semester_id                          |    1 | (NULL)                                             |
|  1 | SIMPLE      | classes_subjects            | ref    | subject_id,class_academic_year_id,class_academic_year_id_subject_id | class_academic_year_id_subject_id | 16      | classes_students.class_academic_year_id,const |    1 | Using index                                        |
|  1 | SIMPLE      | classes_academic_years      | eq_ref | PRIMARY,id_class_id_academic_year_id,id_academic_year_id            | PRIMARY                           | 8       | classes_students.class_academic_year_id       |    1 | Using where                                        |
|  1 | SIMPLE      | professors_classes_subjects | ALL    | (NULL)                                                              | (NULL)                            | (NULL)  | (NULL)                                        |  822 | Using where; Using join buffer (Block Nested Loop) |
|  1 | SIMPLE      | professors_subjects         | ALL    | (NULL)                                                              | (NULL)                            | (NULL)  | (NULL)                                        |  304 | Using where; Using join buffer (Block Nested Loop) |
|  1 | SIMPLE      | professors                  | eq_ref | PRIMARY                                                             | PRIMARY                           | 8       | professors_subjects.professor_id              |    1 | Using index                                        |
|  1 | SIMPLE      | class_masters               | ALL    | (NULL)                                                              | (NULL)                            | (NULL)  | (NULL)                                        |   97 | Using where; Using join buffer (Block Nested Loop) |
+----+-------------+-----------------------------+--------+---------------------------------------------------------------------+-----------------------------------+---------+-----------------------------------------------+------+----------------------------------------------------+

LE:我已经设法通过改进两个LEFT JOIN的ON条件来解决它:

SELECT averages.* FROM `averages` 
INNER JOIN `classes_students` ON classes_students.student_id = averages.student_id 
INNER JOIN `classes_academic_years` ON classes_academic_years.id = classes_students.class_academic_year_id 
INNER JOIN `classes_subjects` ON classes_subjects.class_academic_year_id = classes_academic_years.id 
INNER JOIN `subjects` ON subjects.id = classes_subjects.subject_id 
LEFT JOIN `professors_classes_subjects` ON professors_classes_subjects.class_academic_year_id = classes_subjects.class_academic_year_id AND professors_classes_subjects.subject_id = subjects.id
LEFT JOIN `professors_subjects` ON professors_subjects.professor_id = professors_classes_subjects.professor_id AND professors_subjects.subject_id = subjects.id
LEFT JOIN `professors` ON professors.id = professors_subjects.professor_id 
LEFT JOIN `class_masters` ON class_masters.class_academic_year_id = classes_academic_years.id 
INNER JOIN `semesters` ON semesters.academic_year_id = classes_academic_years.academic_year_id 
INNER JOIN `students` ON students.id = classes_students.student_id 
WHERE (classes_academic_years.academic_year_id = 3) 
AND (subjects.id = 72) 
AND ((professors.id = 198) 
OR (class_masters.id is not null AND class_masters.professor_id = 198)) 
AND (averages.subject_id = subjects.id) 
AND (averages.semester_id = semesters.id) 
ORDER BY `averages`.`student_id` asc, `averages`.`semester_id` asc;

我添加了两个条件(标记为粗体),因此无需使用DISTINCT运算符:

LEFT JOIN professors_classes_subjects ON professors_classes_subjects.class_academic_year_id = classes_subjects.class_academic_year_id AND professors_classes_subjects.subject_id = subjects.id LEFT JOIN professors_subjects ON professors_subjects.professor_id = professors_classes_subjects.professor_id AND professors_subjects.subject_id = subjects.id

1 个答案:

答案 0 :(得分:0)

我设法通过改进两个LEFT JOIN的ON条件来解决它:

SELECT averages.* FROM `averages` 
INNER JOIN `classes_students` ON classes_students.student_id = averages.student_id 
INNER JOIN `classes_academic_years` ON classes_academic_years.id = classes_students.class_academic_year_id 
INNER JOIN `classes_subjects` ON classes_subjects.class_academic_year_id = classes_academic_years.id 
INNER JOIN `subjects` ON subjects.id = classes_subjects.subject_id 
LEFT JOIN `professors_classes_subjects` ON professors_classes_subjects.class_academic_year_id = classes_subjects.class_academic_year_id AND professors_classes_subjects.subject_id = subjects.id
LEFT JOIN `professors_subjects` ON professors_subjects.professor_id = professors_classes_subjects.professor_id AND professors_subjects.subject_id = subjects.id
LEFT JOIN `professors` ON professors.id = professors_subjects.professor_id 
LEFT JOIN `class_masters` ON class_masters.class_academic_year_id = classes_academic_years.id 
INNER JOIN `semesters` ON semesters.academic_year_id = classes_academic_years.academic_year_id 
INNER JOIN `students` ON students.id = classes_students.student_id 
WHERE (classes_academic_years.academic_year_id = 3) 
AND (subjects.id = 72) 
AND ((professors.id = 198) 
OR (class_masters.id is not null AND class_masters.professor_id = 198)) 
AND (averages.subject_id = subjects.id) 
AND (averages.semester_id = semesters.id) 
ORDER BY `averages`.`student_id` asc, `averages`.`semester_id` asc;

我添加了两个条件(标记为粗体),因此无需使用DISTINCT运算符:

LEFT JOIN professors_classes_subjects
  ON professors_classes_subjects.class_academic_year_id = classes_subjects.class_academic_year_id
 AND professors_classes_subjects.subject_id = subjects.id
LEFT JOIN professors_subjects
  ON professors_subjects.professor_id = professors_classes_subjects.professor_id
 AND professors_subjects.subject_id = subjects.id