Mysql 这个多嵌套查询可以优化吗?
我有一个超过10万条记录的表,这个查询需要300个用户(uid),需要30秒才能完成(在我的Drupal网站上) 我正在尝试为每个uid获取: 对于游戏id为0的每个课程id: 所有最佳分数的平均值、总持续时间、计数 对于每个游戏的每个课程\u id(>0): 所有最佳分数的总和,所有第一个分数的总和,总持续时间Mysql 这个多嵌套查询可以优化吗?,mysql,sql,Mysql,Sql,我有一个超过10万条记录的表,这个查询需要300个用户(uid),需要30秒才能完成(在我的Drupal网站上) 我正在尝试为每个uid获取: 对于游戏id为0的每个课程id: 所有最佳分数的平均值、总持续时间、计数 对于每个游戏的每个课程\u id(>0): 所有最佳分数的总和,所有第一个分数的总和,总持续时间 SELECT q1.*, q2.*, q3.* FROM ( SELECT SUM(sq.max_score) / 71 AS avg_max_scores,
SELECT
q1.*,
q2.*,
q3.*
FROM (
SELECT
SUM(sq.max_score) / 71 AS avg_max_scores,
SUM(sq.total_duration) AS sum_total_duration,
SUM(1) AS assessment_count
FROM (
SELECT
up.*,
MAX(up.score) AS max_score,
SUM(up.duration) AS total_duration
FROM cdu_user_progress up
WHERE
(up.uid = '145') AND
(up.lesson_id IN ('65', '66', '67')) AND
(up.score > '-1')
GROUP BY up.lesson_id, up.game_id
) sq
WHERE (sq.game_id = '0')
) q1
INNER JOIN (
SELECT
SUM(sq.max_score) AS sum_max_scores_games,
SUM(sq.total_duration) AS sum_total_duration_games
FROM (
SELECT
up.*,
MAX(up.score) AS max_score,
SUM(up.duration) AS total_duration
FROM cdu_user_progress up
WHERE
(up.uid = '145') AND
(up.lesson_id IN ('65', '66', '67'))
AND (up.score > '-1')
GROUP BY up.lesson_id, up.game_id
) sq
WHERE (sq.game_id > '0')
) q2
INNER JOIN (
SELECT
SUM(q.first_score) / 71 AS avg_first_scores
FROM (
SELECT
sq.max_date AS max_date,
up.score AS first_score
FROM (
SELECT
up.*,
MIN(up.date) AS first_date,
MAX(up.date) AS max_date
FROM cdu_user_progress up
WHERE
(up.uid = '145') AND
(up.lesson_id IN ('65', '66', '67')) AND
(up.score > '-1') AND
(up.game_id = '0')
GROUP BY up.lesson_id
) sq
LEFT OUTER JOIN cdu_user_progress up ON up.lesson_id = sq.lesson_id AND up.game_id = sq.game_id AND up.date = sq.first_date
) q
) q3
解释输出:
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY <derived2> system NULL NULL NULL NULL 1
1 PRIMARY <derived4> system NULL NULL NULL NULL 1
1 PRIMARY <derived6> system NULL NULL NULL NULL 1
6 DERIVED <derived7> ALL NULL NULL NULL NULL 71
7 DERIVED <derived8> ALL NULL NULL NULL NULL 71
7 DERIVED up ref cdu_user_progress(lesson_id, game_id) cdu_user_progress(lesson_id, game_id) 8 sq.lesson_id,sq.game_id 7
8 DERIVED up range cdu_user_progress(uid, lesson_id, level, game_id, ... cdu_user_progress(uid, lesson_id, level, game_id, ... 12 NULL 628 Using where
4 DERIVED <derived5> ALL NULL NULL NULL NULL 90 Using where
5 DERIVED up ref cdu_user_progress(uid, lesson_id, level, game_id, ... cdu_user_progress(uid, lesson_id, level, game_id, ... 4 678 Using where
2 DERIVED <derived3> ALL NULL NULL NULL NULL 90 Using where
3 DERIVED up ref cdu_user_progress(uid, lesson_id, level, game_id, ... cdu_user_progress(uid, lesson_id, level, game_id, ... 4 678 Using where
id选择类型表类型可能的键长度参考行额外
1主系统空1
1主系统空1
1主系统空1
6派生所有空值71
7派生所有空值71
7衍生参考cdu用户进度(课程id,游戏id)cdu用户进度(课程id,游戏id)8 sq.课程id,游戏id 7
8衍生上限cdu用户进度(uid、课程id、级别、游戏id、…cdu用户进度)(uid、课程id、级别、游戏id、…12 NULL 628使用where
4使用where派生所有NULL 90
5派生的参考cdu用户进度(uid、课程id、级别、游戏id、…cdu用户进度)(uid、课程id、级别、游戏id、…4 678使用where
2使用where派生所有NULL 90
3派生出参考cdu用户进度(uid、课程id、级别、游戏id,…cdu用户进度)(uid、课程id、级别、游戏id,…4 678使用where
避免在SQL中嵌套请求,它会降低性能。
如果您没有其他选择,那么使用索引创建一些临时表将更快
比如:
CREATE TABLE pop AS (
SELECT
up.*,
MAX(up.score) AS max_score,
SUM(up.duration) AS total_duration
FROM cdu_user_progress up
WHERE
(up.uid = '145') AND
(up.lesson_id IN ('65', '66', '67')) AND
(up.score > '-1')
GROUP BY up.lesson_id, up.game_id);
然后:
CREATE INDEX index_pop ON pop (game_id);
因此,您的请求看起来像:
SELECT q1.*, q2.*, q3.*
FROM (
SELECT
SUM(sq.max_score) / 71 AS avg_max_scores,
SUM(sq.total_duration) AS sum_total_duration,
SUM(1) AS assessment_count
FROM pop sq
WHERE (sq.game_id = '0')
) q1
INNER JOIN (
SELECT
SUM(sq.max_score) AS sum_max_scores_games,
SUM(sq.total_duration) AS sum_total_duration_games
FROM pop sq
WHERE (sq.game_id > '0')
) q2
INNER JOIN (
SELECT
SUM(q.first_score) / 71 AS avg_first_scores
FROM (
SELECT
sq.max_date AS max_date,
up.score AS first_score
FROM (
SELECT
up.*,
MIN(up.date) AS first_date,
MAX(up.date) AS max_date
FROM cdu_user_progress up
WHERE
(up.uid = '145') AND
(up.lesson_id IN ('65', '66', '67')) AND
(up.score > '-1') AND
(up.game_id = '0')
GROUP BY up.lesson_id
) sq
LEFT OUTER JOIN cdu_user_progress up ON up.lesson_id = sq.lesson_id AND up.game_id = sq.game_id AND up.date = sq.first_date
) q
) q3;
如果在更多的表中剪切请求,则可以将请求执行时间减少更多。
如果您不想保留它们,请记住在之后删除它们。避免在SQL中嵌套请求,否则会降低性能。 如果您没有其他选择,那么使用索引创建一些临时表将更快 比如:
CREATE TABLE pop AS (
SELECT
up.*,
MAX(up.score) AS max_score,
SUM(up.duration) AS total_duration
FROM cdu_user_progress up
WHERE
(up.uid = '145') AND
(up.lesson_id IN ('65', '66', '67')) AND
(up.score > '-1')
GROUP BY up.lesson_id, up.game_id);
然后:
CREATE INDEX index_pop ON pop (game_id);
因此,您的请求看起来像:
SELECT q1.*, q2.*, q3.*
FROM (
SELECT
SUM(sq.max_score) / 71 AS avg_max_scores,
SUM(sq.total_duration) AS sum_total_duration,
SUM(1) AS assessment_count
FROM pop sq
WHERE (sq.game_id = '0')
) q1
INNER JOIN (
SELECT
SUM(sq.max_score) AS sum_max_scores_games,
SUM(sq.total_duration) AS sum_total_duration_games
FROM pop sq
WHERE (sq.game_id > '0')
) q2
INNER JOIN (
SELECT
SUM(q.first_score) / 71 AS avg_first_scores
FROM (
SELECT
sq.max_date AS max_date,
up.score AS first_score
FROM (
SELECT
up.*,
MIN(up.date) AS first_date,
MAX(up.date) AS max_date
FROM cdu_user_progress up
WHERE
(up.uid = '145') AND
(up.lesson_id IN ('65', '66', '67')) AND
(up.score > '-1') AND
(up.game_id = '0')
GROUP BY up.lesson_id
) sq
LEFT OUTER JOIN cdu_user_progress up ON up.lesson_id = sq.lesson_id AND up.game_id = sq.game_id AND up.date = sq.first_date
) q
) q3;
如果在更多的表中剪切请求,则可以将请求执行时间减少更多。
如果你不想保留它们,请记住在之后删除它们你应该真正决定是使用MySQL还是Oracle,并适当地标记问题。你检查过解释计划了吗?你应该真正决定是使用MySQL还是Oracle,并适当地标记问题。你检查过解释计划了吗?