我有一个非常大的选择,有点慢,我想帮助改善它。
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 ?
我应该在哪里添加索引来加速我的选择?
在on
和where
的所有字段中?比如:p.user, c.id, pp.user, p.delete
...是不是太多了?
答案 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.nome
和c.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