MySql多次连接需要很长时间(只有一个表很大)

时间:2016-01-07 12:39:55

标签: mysql sql

任何人都可以帮助我提高查询效率,有时需要3秒才能执行:

SELECT 
    up.*, 
    u.name AS learner_name, 
    g.type AS group_type, 
    l.name AS name, 
    l.type AS type, 
    l.assessment_type AS assessment_type, 
    l.id AS lesson_id, 
    s.sort AS sort 
FROM 
    cdu_user_progress up 
INNER JOIN cdu_groups g ON g.id = up.group_id 
INNER JOIN cdu_sessions s ON s.id = up.session_id 
INNER JOIN cdu_lessons l ON l.id = up.lesson_id 
INNER JOIN users u ON u.uid = up.uid 
INNER JOIN field_data_field_teacher_id ftid ON ftid.entity_id = up.uid 
WHERE 
    (ftid.field_teacher_id_value = '6378') 
ORDER BY 
    up.id DESC 
LIMIT 15 
OFFSET 0

cdu_user_progress(up)是BIG表,它包含> 200k行。其他表只包含几百行。

解释返回:

id |select_type |table |type   |possible_keys                            |key                                   |key_len |ref                         |rows |Extra
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
1  |SIMPLE      |l     |ALL    |PRIMARY                                  |NULL                                  |NULL    |NULL                        |744  |Using temporary; Using filesort
1  |SIMPLE      |up    |ref    |cdu_user_progress(uid, lesson_id, level, |cdu_user_progress(lesson_id, game_id) |4       |ealteach_main.l.id          |133  |
   |            |      |       |game_id, date),cdu_user_progress(uid,    |                                      |        |                            |     |
   |            |      |       |lesson_id, level, game_id, score),       |                                      |        |                            |     |
   |            |      |       |cdu_user_progress(lesson_id, game_id),   |                                      |        |                            |     |
   |            |      |       |idx_score,idx_id_update                  |                                      |        |                            |     |
1  |SIMPLE      |g     |eq_ref |PRIMARY                                  |PRIMARY                               |4       |ealteach_main.up.group_id   |1    |
1  |SIMPLE      |ftid  |ref    |entity_id                                |entity_id                             |4       |ealteach_main.up.uid        |1    |Using index condition; Using where
1  |SIMPLE      |u     |eq_ref |PRIMARY                                  |PRIMARY                               |4       |ealteach_main.up.uid        |1    |Using where
1  |SIMPLE      |s     |eq_ref |PRIMARY                                  |PRIMARY                               |4       |ealteach_main.up.session_id |1    |

不确定我是否正确阅读,但它似乎正在扫描整个cdu_lessons(l)表 - 这是瓶颈的位置吗?

希望有人可以提供帮助!

2 个答案:

答案 0 :(得分:1)

执行INNER JOINS时,请确保首先包含其他表中的列,然后是联接表列。这会将您的查询优化10-20%,对于其他查询也是一种很好的做法,例如:

INNER JOIN cdu_groups g ON up.group_id = g.id 
INNER JOIN cdu_sessions s ON up.session_id = s.id 

等等其他表..

答案 1 :(得分:0)

我们认为表格cdu_user_progress很大,并且您的查询中只有一件事情可以减少要从中读取的行:必须存在field_data_field_teacher_id条记录field_teacher_id_value = '6378'才能显示

然而,解释计划显示DBMS首先加入课程和组(对于所有 cdu_user_progress记录)。我想知道为什么会这样。现场教师的事情缩小了多少结果?你得到cdu_user_progress记录的1%吗?或者90%DBMS似乎认为它可能很多,所以它不打算使用这个标准来缩小中间结果。也许真的是你会得到许多记录,然后DBMS是正确的,这根本无法快速完成。但是,如果只有几行并且DBMS是错误的,那么我们必须确保DBMS知道现场教师标准的限制性。

首先:我从field_data_field_teacher_id获得了多少field_teacher_id_value条记录?在该字段中添加索引,以便使DBMS知道该分发。它只是每个ID值一个记录吗?那么这是一个唯一的密钥,应该得到一个唯一的索引。这将是一个显而易见的事情。对于另一种情况,当ID值获得2或10或100条记录时,您将拥有非唯一索引,而DBMS尚不知道该分布。创建索引后,您必须运行ANALYZE TABLE,因此DBMS会存储所需的统计数据。

因为它只是存在您感兴趣的匹配field_data_field_teacher_id记录,所以您也应该在查询中明确这一点。使用EXISTS或IN(或者甚至=,只能有一个匹配的记录)。

SELECT 
    up.*, 
    u.name AS learner_name, 
    g.type AS group_type, 
    l.name AS name, 
    l.type AS type, 
    l.assessment_type AS assessment_type, 
    l.id AS lesson_id, 
    s.sort AS sort 
FROM 
    cdu_user_progress up 
INNER JOIN cdu_groups g ON g.id = up.group_id 
INNER JOIN cdu_sessions s ON s.id = up.session_id 
INNER JOIN cdu_lessons l ON l.id = up.lesson_id 
INNER JOIN users u ON u.uid = up.uid 
WHERE up.uid IN
(
  SELECT ftid.entity_id
  FROM field_data_field_teacher_id ftid
  WHERE ftid.field_teacher_id_value = '6378'
)
ORDER BY 
    up.id DESC 
LIMIT 15 
OFFSET 0;

(顺便说一句,为什么你在数字上使用引号?不是field_teacher_id_value数字吗?它是否也包含非数字ID值?)

然后uid限制从表cdu_user_progress中读取的行。因为它是一个ID,肯定已经有一个索引,对吧? (每个外键都应该有一个索引。)再次,我用一个uid得到多少条记录?要使DBMS知道这一点,请在ANALYZE TABLE上运行cdu_user_progress

如果这一切都无济于事,您可能希望在查询中明确指出:

SELECT 
    up.*, 
    u.name AS learner_name, 
    g.type AS group_type, 
    l.name AS name, 
    l.type AS type, 
    l.assessment_type AS assessment_type, 
    l.id AS lesson_id, 
    s.sort AS sort 
FROM 
(
  SELECT *
  FROM cdu_user_progress
  WHERE uid IN
  (
    SELECT ftid.entity_id
    FROM field_data_field_teacher_id ftid
    WHERE ftid.field_teacher_id_value = '6378'
  )
  ORDER BY up.id DESC 
  LIMIT 15 
) up
INNER JOIN cdu_groups g ON g.id = up.group_id 
INNER JOIN cdu_sessions s ON s.id = up.session_id 
INNER JOIN cdu_lessons l ON l.id = up.lesson_id 
INNER JOIN users u ON u.uid = up.uid 
ORDER BY up.id DESC;

(好吧,无论如何,你可能想要这样做,因为它非常清楚地显示了你在做什么,可能有助于维护。)

以下是MySQL中ANALYZE TABLE的说明:http://dev.mysql.com/doc/refman/5.7/en/analyze-table.html

相关问题