大选中的索引 - mysql

时间:2018-05-04 10:34:26

标签: mysql indexing

我有一个非常大的选择,有点慢,我想帮助改善它。

select c.nome, p.foto, c.user, p.user, p.id, p.data, p.titulo, p.youtube, pp.foto, count(DISTINCT likes.user) as likes_count, count(distinct comentarios.id) as comentarios_count, count(DISTINCT l2.user) as count2

from posts p 

join cadastro c on p.user=c.id 
left join profile_picture pp on p.user = pp.user
left join likes on likes.post = p.id
left join comentarios on comentarios.foto = p.id and comentarios.delete = 0  
left join likes l2 on l2.post = p.id and l2.user = ?

where p.user=? and p.delete='0'
group by p.id
order by p.id limit ?

我应该在哪里添加索引来加速我的选择? 在onwhere的所有字段中?比如:p.user, c.id, pp.user, p.delete ...是不是太多了?

2 个答案:

答案 0 :(得分:2)

加速此查询的一个好方法是重构它以执行延迟连接。目标是使用尽可能少的列数对结果集执行SELECT ... ORDER BY ... LIMIT...操作。为什么这很重要?订购大结果集比订购小结果集更昂贵,尤其是当LIMIT丢弃大部分订购结果时。

所以,从这个子查询开始:

               SELECT p.id, c.id
                 FROM posts p
                 JOIN cadastro c ON p.user=c.id 
                WHERE p.user=? and p.delete='0'
                ORDER BY p.id
                LIMIT ?

您的查询中包含相关的posts.id和cadastro.id值。您可以使用posts(user, delete)上的compound covering index加快速度:查询计划程序可以通过扫描部分复合索引来完全满足此子查询。

然后将其加入主查询版本。

    SELECT c.nome, p.foto, c.user, p.user, p.id, p.data, p.titulo, 
           p.youtube, pp.foto, 
           count(DISTINCT likes.user) as likes_count,
           count(distinct comentarios.id) as comentarios_count,
           count(DISTINCT l2.user) as count2
    FROM (
                   SELECT p.id pid, c.id cid
                     FROM posts p
                     JOIN cadastro c ON p.user=c.id 
                    WHERE p.user=? and p.delete='0'
                    ORDER BY p.id, c.id
                    LIMIT ?
         ) selector
    JOIN posts p ON selector.pid = p.id
    JOIN cadastro c ON selector.cid =  p.user
    left join profile_picture pp on p.user = pp.user
    left join likes on likes.post = p.id
    left join comentarios on comentarios.foto = p.id and comentarios.delete = 0  
    left join likes l2 on l2.post = p.id and l2.user = ?
   where p.user=? and p.delete='0'
   group by p.id
   order by p.id limit ?

您需要重做ORDER BY ... LIMIT ?操作,因为您的左连接可能会增加最终结果集的大小,您需要限制它。

如果没有关于表的更多信息,很难说哪些索引会加快查询的其余部分。所有那些COUNT(DISTINCT ......)操作都不可避免地有些昂贵。阅读本文可能会让您受益:https://use-the-index-luke.com/

专业提示您正在使用并可能滥用a notorious extension to GROUP BY in MySQL。您的GROUP BY应该说明这一点,或者可能以不可预测的方式选择值c.nomec.user

GROUP BY p.id, c.id

专业提示单列索引通常无法帮助查询或子查询:MySQL在查询中每个表只能使用一个索引。所以,covering indexes with the columns in the right order可以提供很多帮助。不要只是放在一堆希望加速查询的索引中。

答案 1 :(得分:1)

按此顺序在post上添加综合索引

post:  INDEX(user, delete, id)
profile_picture:  (user, foto)
likes:  (post, user)
commentarios:  (foto, delete, id)

如果我理解'发布'和cadastro(注册表),每个帖子将是一个cadastro条目?因此,不需要在派生表中包含cadastro。

另外,我假设每人最多有一张照片。 (否则GROUP BY会遇到麻烦,而O.Jones将无法得到正确的答案。)如果可以有多个,但只想显示一个,则有一个修复。 (使用MAX。)

我在SELECT子句中使用子查询以避免JOIN...GROUP BY的爆炸性内爆。

我不清楚l2.user = ?的意图,但我一言不发。

SELECT  c.nome, p.foto, c.user, p.user, p.id, p.data, p.titulo,
        p.youtube,
        ( SELECT MAX(foto) FROM profile_picture
                           WHERE p.user = user ) AS foto,
        ( SELECT count(DISTINCT user) FROM likes
                           WHERE post = p.id ) as likes_count,
        ( SELECT count(distinct id) FROM comentarios
                           WHERE foto = p.id
                             AND delete  = 0 ) as comentarios_count,
        ( SELECT count(DISTINCT user) FROM likes
                           WHERE post = p.id
                             AND user = ? ) as count2
    FROM  
    (
        SELECT  p.id pid
            FROM  posts p
            WHERE  p.user=?
              and  p.delete='0'
            ORDER BY  p.id
            LIMIT  ? 
    ) selector
    JOIN  posts p  ON selector.pid = p.id
    JOIN  cadastro c  ON p.user = c.id
    ORDER BY  p.id