Mysql 这个多嵌套查询可以优化吗?

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,

我有一个超过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, 
    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,并适当地标记问题。你检查过解释计划了吗?