任何人都可以帮助我提高查询效率,有时需要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)表 - 这是瓶颈的位置吗?
希望有人可以提供帮助!
答案 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