mysql查询优化步骤或如何优化查询

时间:2015-02-18 08:28:54

标签: mysql sql

我对查询优化了解不多,但我知道执行查询的顺序

  1. FROM clause
  2. WHERE子句
  3. GROUP BY子句
  4. HAVING条款
  5. SELECT条款
  6. ORDER BY子句
  7. 这是我写的查询

    SELECT 
        `main_table`.forum_id,
         my_topics.topic_id,
         (
            SELECT MAX(my_posts.post_id) FROM my_posts WHERE my_topics.topic_id = my_posts.topic_id 
         ) AS `maxpostid`,
         (
           SELECT my_posts.admin_user_id FROM my_posts WHERE my_topics.topic_id = my_posts.topic_id ORDER BY my_posts.post_id DESC LIMIT 1
         ) AS `admin_user_id`, 
         (
           SELECT my_posts.user_id FROM my_posts WHERE my_topics.topic_id = my_posts.topic_id ORDER BY my_posts.post_id DESC LIMIT 1
         ) AS `user_id`,
         (
          SELECT COUNT(my_topics.topic_id) FROM my_topics WHERE my_topics.forum_id = main_table.forum_id ORDER BY my_topics.forum_id DESC LIMIT 1
         ) AS `topicscount`,
         (
          SELECT COUNT(my_posts.post_id) FROM my_posts WHERE my_topics.topic_id = my_posts.topic_id ORDER BY my_topics.topic_id DESC LIMIT 1
         ) AS `postcount`, 
         (
          SELECT CONCAT(admin_user.firstname,' ',admin_user.lastname) FROM admin_user INNER JOIN my_posts ON my_posts.admin_user_id = admin_user.user_id WHERE my_posts.post_id = maxpostid ORDER BY my_posts.post_id DESC LIMIT 1
         ) AS `adminname`, 
        (
         SELECT forum_user.nick_name FROM forum_user INNER JOIN my_posts ON my_posts.user_id = forum_user.user_id WHERE my_posts.post_id = maxpostid ORDER BY my_posts.post_id DESC LIMIT 1
        ) AS `nickname`, 
        (
          SELECT CONCAT(ce1.value,' ',ce2.value) AS fullname FROM my_posts INNER JOIN customer_entity_varchar AS ce1 ON ce1.entity_id = my_posts.user_id INNER JOIN customer_entity_varchar AS ce2 ON ce2.entity_id=my_posts.user_id WHERE (ce1.attribute_id = 1) AND (ce2.attribute_id = 2) AND my_posts.post_id = maxpostid ORDER BY my_posts.post_id DESC LIMIT 1
        ) AS `fullname`
        FROM `my_forums` AS `main_table`
           LEFT JOIN `my_topics` ON main_table.forum_id = my_topics.forum_id 
        WHERE  (forum_status = '1')
    

    现在我想知道是否有任何方法可以优化它?因为所有逻辑都是用Select部分而不是From编写的,但我不知道如何在查询的From部分编写相同的逻辑?

    是否有任何区别或两者相同?

    由于

1 个答案:

答案 0 :(得分:2)

相关的子查询应该是最后的手段,它们通常最终会被执行RBAR,并且考虑到许多子查询非常相似,尝试使用连接获得相同的结果将导致更少的表扫描

我注意到的第一件事是你的所有子查询都包含表my_posts,大多数包含ORDER BY my_posts.post_id DESC LIMIT 1,那些没有计数但没有组的那些,所以顺序和限制是无论如何多余,所以我的第一步是加入my_posts:

SELECT  *
FROM    my_forums AS f
        LEFT JOIN my_topics AS t
            ON f.forum_id = t.forum_id 
        LEFT JOIN
        (   SELECT  topic_id, MAX(post_id) AS post_id
            FROM my_posts 
            GROUP BY topic_id
        ) AS Maxp
            ON Maxp.topic_id = t.topic_id 
        LEFT JOIN my_posts AS p
            ON p.post_id = Maxp.post_id
WHERE   forum_status = '1';

此子查询只是确保您获得每个topic_id的最新帖子。为方便起见,我在这里缩短了表别名,我不确定为什么要使用比实际表名更长的表别名?

现在你有大量的查询可以开始在你的列中添加,为了获得帖子计数,我已经为子查询添加了一个计数Maxp,我还需要添加更多加入以获取一些细节,例如名称:

SELECT  f.forum_id,
        t.topic_id,
        p.post_id AS `maxpostid`,
        p.admin_user_id,
        p.user_id,
        t2.topicscount,
        maxp.postcount,
        CONCAT(au.firstname,' ',au.lastname) AS adminname, 
        fu.nick_name AS nickname
        CONCAT(ce1.value,' ',ce2.value) AS fullname 
FROM    my_forums AS f
        LEFT JOIN my_topics AS t
            ON f.forum_id = t.forum_id 
        LEFT JOIN
        (   SELECT  topic_id, 
                    MAX(post_id) AS post_id,
                    COUNT(*) AS postcount
            FROM my_posts 
            GROUP BY topic_id
        ) AS Maxp
            ON Maxp.topic_id = t.topic_id 
        LEFT JOIN my_posts AS p
            ON p.post_id = Maxp.post_id
        LEFT JOIN admin_user AS au
            ON au.admin_user_id = p.admin_user_id
        LEFT JOIN forum_user AS fu
            ON fu.user_id = p.user_id
        LEFT JOIN customer_entity_varchar AS ce1
            ON ce1.entity_id = p.user_id
            AND ce1.attribute_id = 1
        LEFT JOIN customer_entity_varchar AS ce2
            ON ce2.entity_id = p.user_id
            AND ce2.attribute_id = 2
        LEFT JOIN
        (   SELECT  forum_id, COUNT(*) AS topicscount
            FROM    my_topics
            GROUP BY forum_id
        ) AS t2
            ON t2.forum_id = f.forum_id
WHERE   forum_status = '1';

我不熟悉您的架构,所以上面可能需要一些调整,但主要仍然是 - 使用JOIN而不是子选择。

我要做的下一个优化阶段是摆脱你的customer_entity_varchar表,或至少停止使用它来存储基本的名字和姓氏。 Entity-Attribute-Value模型是一个SQL反模式,如果您向FirstName表添加了两列LastNameforum_user,您将立即从查询中丢失两个连接。我不会过多地参与EAV vs Relational debate,因为这已被多次广泛讨论过,我没有其他任何补充。

最后一个阶段是添加适当的索引,你最好决定什么是合适的,我建议你可能至少需要索引每个表中的外键,可能更多。


修改

要获得每forum_id行一行,您需要使用以下内容:

SELECT  f.forum_id,
        t.topic_id,
        p.post_id AS `maxpostid`,
        p.admin_user_id,
        p.user_id,
        MaxT.topicscount,
        maxp.postcount,
        CONCAT(au.firstname,' ',au.lastname) AS adminname, 
        fu.nick_name AS nickname
        CONCAT(ce1.value,' ',ce2.value) AS fullname 
FROM    my_forums AS f
        LEFT JOIN
        (   SELECT  t.forum_id, 
                    COUNT(DISTINCT t.topic_id) AS topicscount,
                    COUNT(*) AS postCount,
                    MAX(t.topic_ID) AS topic_id
            FROM    my_topics AS t
                    INNER JOIN my_posts AS p
                        ON p.topic_id = p.topic_id
            GROUP BY t.forum_id
        ) AS MaxT
            ON MaxT.forum_id = f.forum_id
        LEFT JOIN my_topics AS t
            ON t.topic_ID = Maxt.topic_ID 
        LEFT JOIN
        (   SELECT  topic_id, MAX(post_id) AS post_id
            FROM    my_posts 
            GROUP BY topic_id
        ) AS Maxp
            ON Maxp.topic_id = t.topic_id 
        LEFT JOIN my_posts AS p
            ON p.post_id = Maxp.post_id
        LEFT JOIN admin_user AS au
            ON au.admin_user_id = p.admin_user_id
        LEFT JOIN forum_user AS fu
            ON fu.user_id = p.user_id
        LEFT JOIN customer_entity_varchar AS ce1
            ON ce1.entity_id = p.user_id
            AND ce1.attribute_id = 1
        LEFT JOIN customer_entity_varchar AS ce2
            ON ce2.entity_id = p.user_id
            AND ce2.attribute_id = 2
WHERE   forum_status = '1';