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

mysql查询优化步骤或如何优化查询,mysql,sql,Mysql,Sql,我对查询优化知之甚少,但我知道查询的执行顺序 FROM子句 WHERE子句 分组依据子句 有从句 SELECT子句 按条款订货 这就是我写的问题 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 `ma

我对查询优化知之甚少,但我知道查询的执行顺序

  • FROM子句
  • WHERE子句
  • 分组依据子句
  • 有从句
  • SELECT子句
  • 按条款订货
  • 这就是我写的问题

    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
    section而不是
    From
    ,但是我不知道如何在查询的
    From
    部分中写相同的逻辑

    这有什么区别吗?或者两者都一样


    多亏了

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

    我注意到的第一件事是,您的所有子查询都包括表
    my_posts
    ,并且大多数都包含按my_posts排序的
    ORDER.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';
    
    在这里,子查询只是确保您获得每个主题id的最新帖子。为了方便起见,我在这里缩短了您的表别名,我不确定您为什么要使用比实际表名长的表别名

    现在您有了大量的查询,您可以开始在列中添加,为了获得post计数,我在子查询
    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';
    
    我不熟悉您的模式,因此上面的内容可能需要一些调整,但主要内容仍然是-在子选择上使用连接

    我要做的下一个优化阶段是去掉
    customer\u entity\u varchar
    表,或者至少停止使用它来存储诸如名字和姓氏之类的基本内容。实体属性值模型是一个SQL反模式,如果在
    论坛用户
    表中添加两列,
    FirstName
    LastName
    ,则会立即丢失查询中的两个联接。我不会参与太多的讨论,因为这已经被广泛讨论了很多次,我没有更多的补充

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


    编辑

    要为每个
    论坛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';
    

    据我所知,MySQL用户在性能方面更喜欢连接,而不是相关的子选择。有没有可能重写为join with GROUP BY?@jarlh我想你错过了问题“但我不知道如何在查询的From部分编写相同的逻辑?”;)