2014-01-22 4 views
1

У меня есть следующий запрос, который объединяет 5 связанных с InnoDB таблиц, чтобы получить желаемый набор результатов из 10 строк. Я сделал все возможное, чтобы решить проблему, добавив индексы и переписав запрос разными способами, но Я закончил либо неожиданный результат, либо очень медленный запрос.Улучшение производительности MySQL Query

ЗДЕСЬ В запросе

SELECT 
    a.*, 
    c.id as category_id, 
    c.title as catname, 
    CONCAT(u.fname, ' ', u.lname) as username, 
    DATE_FORMAT(a.created, '%W %M %d, %Y - %T') as long_date, 
    DATE_FORMAT(a.created, '%d/%m/%Y - %T') as short_date, 
    (SELECT 
      COUNT(article_id) 
     FROM 
      comment 
     WHERE 
      article_id = a.id) as totalcomments, 
    YEAR(a.created) as year, 
    MONTH(a.created) as month, 
    DAY(a.created) as day 
FROM 
    article as a 
     INNER JOIN 
    article_related_categories rc ON a.id = rc.article_id 
     LEFT JOIN 
    category as c ON c.id = rc.category_id 
     LEFT JOIN 
    user as u ON u.id = a.user_id 
WHERE 
    rc.category_id = 1 
     AND a.created <= NOW() 
     AND (a.expire = '0000-00-00 00:00:00' 
     OR a.expire >= NOW()) 
     AND a.published IS NOT NULL 

ORDER BY a.created DESC 
LIMIT 0 , 10 

Click Here to see the explain screenshot

В настоящее время существует более 13 000 строк в таблице статьи и быстрый рост, как ожидается.

Проблема в том, что этот запрос может занять значительное количество времени, и это занимает около 3-4 секунд. Я подозреваю, что INNER JION вызывает большую часть проблемы, но я подумал, что спрошу здесь, есть ли у кого-нибудь идеи по улучшению производительности этого запроса.

+0

Где rc.category_id = 1 ну, вы начинаете с связанного файла категорий, потому что вы уже сузились до одного. Надеюсь, это имеет смысл. выберите blah из related_category rc join x на rc.id = x.id где rc.category_id = 1 – danny117

ответ

0

Вложенные SELECT могут замедлять работу. Регистрация на comment стол и GROUP BY a.id:

... 
    COUNT(*) as totalcomments, 
...  
FROM 
    ... 
    LEFT JOIN comment AS comm.article_id = a.id 
WHERE 
    ... 
GROUP BY a.id 
0

Ну быстрое решение, чтобы избавиться от этого

AND a.created <= NOW() 

, потому что статья создана в будущем действительно не имеет смысла. Еще одна вещь, которую обычно делает db (почти всегда), приводит к более быстрому выполнению.

Трудность в ответе - это не знать, что вы действительно хотите получить от db. Вам нужно подумать о своих левых соединениях и устранить их там, где это применимо. Проблема в том, что вы не устраняете строки с левым соединением и меньшими наборами результатов, такими как те, которые вы получаете, устраняя строки, быстрее возвращаются, потому что результирующий набор меньше.

Для оптимальной скорости я бы начал с таблицы связанных категорий, потому что у меня есть результаты, суженные в инструкции where до 1, и я просматриваю только одно значение для связанной_категории.

select blah from related_categories rc 
join comment c on r.id = c.id 
join blah b on b.id = c.id 
where rc.id = 1 
0

Я бы иметь индексы таблиц

article table index -- (published, expire, id) 
article table index -- (id) just the primary key ID for secondary join criteria 
article_related_categories table index(article_id, category_id) 
comment table (article_id) 

Тогда есть предварительный запрос не делать ничего, кроме получить идентификатор и статьи, и рассчитывает на соответствующей категории интересов, порядка и предела к 10 статьям ... ТОГДА присоединиться к категории и пользователям таблица для вашего окончательного вывода.

SELECT 
     a2.*, 
     c.id as category_id, 
     c.title as catname, 
     CONCAT(u.fname, ' ', u.lname) as username, 
     DATE_FORMAT(a2.created, '%W %M %d, %Y - %T') as long_date, 
     DATE_FORMAT(a2.created, '%d/%m/%Y - %T') as short_date, 
     PreQual.TotalComments, 
     YEAR(a2.created) as year, 
     MONTH(a2.created) as month, 
     DAY(a2.created) as day 
    from 
     (select 
       a.id, 
       rc.category_id, 
       COUNT(c.article_id) as TotalComments 
      from 
       article a 
       join article_related_categories rc 
        ON a.id = rc.article_id 
        AND rc.category_id = 1 
       left join comment c 
        ON a.id = c.article_id 
      where 
        a.published IS NOT NULL 
       AND ( a.expire >= now() 
        OR a.expire = '0000-00-00 00:00:00') 
      group by 
       a.id, 
       rc.category_id 
      order by 
       a.created DESC 
      limit 
       0, 10) PreQual 
     JOIN article a2 
      ON PreQual.ID = a2.id 
      LEFT JOIN user u 
       ON a2.user_id = u.id 
     LEFT JOIN category as c 
      ON PreQual.Category_ID = c.id 

Теперь, даже с вышеупомянутым запросом, делая веб-активность (который оказывается), и делать подсчеты от всего подмножества на коррелированном состоянии может быть ОГРОМНЫМ снижением производительности. Вам было бы лучше DE-нормализовать данные с уважением. В таблице статей добавьте столбец для CommentCount. Затем, когда добавляется любой комментарий, есть триггер после вставки на комментарий, который в основном делает

update Articles 
    set CommentCount = CommentCount +1 
    where id = the article ID of the new comment ID just inserted. 

Тогда вы никогда не должны вернуться назад и сделать COUNT() каждый раз. Это будет ваш лучший оперативный ход. Вам нужно будет по умолчанию использовать все счеты до создания триггера, но это будет одноразовое коррелированное обновление счетчиков. Вам просто нужно вернуться к таблице соответствующих статей, чтобы соответствовать вашим критериям категории.

Смежные вопросы