Индексы в большом выборе - mysql

0

У меня действительно большой выбор, который немного медленный, и я бы хотел помочь ему улучшить его.

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
    Пожалуйста, прочитайте эти советы, чтобы задать хороший вопрос SQL . Обратите особое внимание на раздел о производительности запросов. Затем, пожалуйста, отредактируйте свой вопрос, чтобы дать нам больше информации, чтобы мы могли вам помочь.
  • 0
    сколько строк у вас в таблицах? Планируете ли вы использовать этот запрос только с limit или это только первый образец нумерации страниц, чтобы позже получить другие части со смещением?
Теги:
indexing

2 ответа

1
Лучший ответ

Добавьте объединенный индекс в post в следующем порядке:

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

Если я понимаю "пост" и кадастро (реестр), будет ли запись кадастра для каждого сообщения? Поэтому нет необходимости включать кадастро в производную таблицу.

Кроме того, я предполагаю, что на человека есть не более одного фото. (В противном случае GROUP BY находится в неисправности, а O.Jones не получит правильного ответа.) Есть исправление, если их может быть несколько, но вы хотите показать только одно. (Используйте MAX.)

Я использую подзапросы в предложении SELECT, чтобы избежать взрыва-implode 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
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): планировщик запросов может полностью удовлетворить этот подзапрос от проверки части этого составного индекса.

Затем вы присоединитесь к этой версии вашего основного запроса.

    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/

Pro tip Вы используете и, возможно, неправильно используете пресловутое расширение GROUP BY в MySQL. Ваша GROUP BY должна сказать это, или значения c.nome и c.user могут быть выбраны непредсказуемым образом.

GROUP BY p.id, c.id

Pro tip Одноколоночные индексы обычно не помогают в запросах или подзапросах: MySQL может использовать только один индекс для каждой таблицы в запросе. Таким образом, покрытие индексов столбцами в правильном порядке может помочь. Не просто вставляйте кучу индексов в надежде ускорить запросы.

  • 0
    спасибо большое, попробую ваши советы! очень помог мне

Ещё вопросы

Сообщество Overcoder
Наверх
Меню