2015-06-15 5 views
0

У меня есть большой запрос MySQL, который фактически возвращает хороший набор результатов, но он довольно медленный.Как улучшить список моих друзей MySQL?

SELECT DISTINCT 
    username, 
    user.id as uid, 
    email, 
    ui.gender, 
    ui.country, 
    ui.birthday, 
    IF(last_activity_date >= now() - INTERVAL 1 HOUR, 1, 0) as is_online, 
    p.thumb_url, 
    friend_request.id as sr_id, 
    IF(ul.id IS NOT NULL, 1, 0) as st_love, 
    DATE_FORMAT(NOW(), '%Y') - DATE_FORMAT(birthday, '%Y') - (DATE_FORMAT(NOW(), '00-%m-%d') < DATE_FORMAT(birthday, '00-%m-%d')) AS age 

FROM friend_request 

JOIN user ON (user.`id` = friend_request.`to_user_id` 
        OR 
        user.`id` = friend_request.`from_user_id`) 
       AND user.`id` != '$user_id' 
       AND friend_request.`status` = '1' 

JOIN user_info ui ON user.`id` = ui.`user_id` 

JOIN photo p ON ui.`main_photo` = p.`id` 

LEFT JOIN user_love ul ON ul.`to_user_id` = user.`id` 
         AND ul.`from_user_id` = $user_id 

WHERE (friend_request.`to_user_id` = '$user_id' 
    OR friend_request.`from_user_id` = '$user_id') 
ORDER BY friend_request.id DESC 
LIMIT 30 

"$ user_id" является идентификатором зарегистрированного пользователя.

Вот структуру таблиц «friend_request»:

CREATE TABLE `friend_request` (
    `id` int(11) unsigned NOT NULL AUTO_INCREMENT, 
    `from_user_id` int(11) DEFAULT NULL, 
    `to_user_id` int(11) DEFAULT NULL, 
    `date` datetime DEFAULT NULL, 
    `seen` int(11) DEFAULT '0', 
    `status` int(11) DEFAULT '0', 
    PRIMARY KEY (`id`), 
    KEY `from_user_id` (`from_user_id`), 
    KEY `to_user_id` (`to_user_id`) 
) ENGINE=InnoDB DEFAULT CHARSET=latin1 ; 

Можете ли вы помочь мне улучшить этот запрос?

Я не скопировал структуру таблицы других таблиц, потому что после некоторых тестов «проблема оптимизации», похоже, поступает из таблицы friend_request.

Спасибо!

EDIT:

Вот что "EXPLAIN" дает мне:

enter image description here

+0

Рассмотрите возможность предоставления набора данных и желаемого набора результатов. – Strawberry

+2

В ЛЮБОМ СОЕДИНЕНИИ, а не ВПРАВО. (Конечно, вы должны переключить таблицы слева <-> справа тоже ...) Это реальное улучшение удобочитаемости! (У большинства людей достаточно проблем с левым соединением и слишком запутаны, когда дело доходит до правильного соединения ...) – jarlh

+0

Вы называете это из приложения? Возможно, вы можете сделать некоторую бизнес-логику (все операторы if/then и преобразования даты) в своем коде приложения и передать параметризованный список на MySql, чтобы облегчить часть обработки в базе данных. И я согласен с @jarlh использовать левые соединения вместо правого. – ragerory

ответ

1

Вы должны смотреть на план запроса или попытаться использовать ключевое слово Explainhttps://dev.mysql.com/doc/refman/5.5/en/using-explain.html так что вы можете найти какие части вашего запроса занимают дольше и оптимизируют их.

Вещи, которые выпрыгивают мне:

1) Вы, возможно, потребуется уменьшить ваши соединения, или оптимизировать их

2) Вы, возможно, потребуется несколько индексов

3) У вас есть OR заявление в вашем where, который может повлиять на план запроса кеша, я видел, что Or вызывает проблемы с кешированием запросов в tsql. Не уверен, повлияет ли это на mysql. https://dev.mysql.com/doc/refman/5.1/en/query-cache.html

редактировать: форматирование, выясняли таблицу фотографий присоединиться необходимо для данных, который был выбран

+0

ah спасибо, пропустил это за этот длинный выбор –

+0

Спасибо за ваш ответ. 1) -> связан со вторым оператором IF моего запроса. По предложению другого пользователя здесь я работаю над перемещением операторов IF из запроса, поэтому мне больше не понадобится эта часть запроса. Это может сэкономить несколько мс. 2) У меня уже есть индексы на таблицах. Но, может быть, я тоже смог бы улучшить эту часть. – fraxool

+0

@fraxool Настоящая ключевая вещь здесь - использовать ключевое слово «Объяснить», каждый запрос должен быть оптимизирован с большей информацией, чем вы предоставили, а работа с ключом объяснения учитывает эти вещи (индексы, типы объединений, и т.д). Мы должны играть в догадки и проверки, не пытаясь их использовать. –

0

Я бы написать запрос, как это. (Я бы также квалифицировал ссылки на все столбцов в запросе, username, email, last_activity_date). Я также делал бы это как подготовленный оператор со связями с привязкой, вместо того, чтобы включать переменную $user_id в текст SQL. Мы предполагаем, что содержание $user_id, как известно, является «безопасным» или было надлежащим образом экранировано, чтобы избежать инъекции SQL.

SELECT username 
     , user.id AS uid 
     , email 
     , ui.gender 
     , ui.country 
     , ui.birthday 
     , IFNULL((last_activity_date >= NOW() - INTERVAL 1 HOUR),0) AS is_online 
     , p.thumb_url 
     , sr.id          AS sr_id 
     , ul.id IS NOT NULL       AS st_love 
     , TIMESTAMPDIFF(YEAR,ui.birthday,DATE(NOW())) AS age 
    FROM (
      (SELECT frf.id 
        , frf.to_user_id AS user_id 
       FROM friend_request frf 
       WHERE frf.to_user_id <> '$user_id' 
       AND frf.from_user_id = '$user_id' 
       AND frf.status = '1' 
       ORDER BY frf.id DESC 
       LIMIT 30 
      ) 
      UNION ALL 
      (SELECT frt.id 
        , frt.from_user_id AS user_id 
       FROM friend_request frt 
       WHERE frt.from_user_id <> '$user_id' 
       AND frt.to_user_id = '$user_id' 
       AND frt.status = '1' 
       ORDER BY frt.id DESC 
       LIMIT 30 
      ) 
      ORDER BY id DESC 
      LIMIT 30 
     ) sr 
    JOIN user ON user.id = sr.user_id 
    JOIN user_info ui ON ui.user_id = user.id 
    JOIN photo p ON p.id = ui.main_photo 
    LEFT 
    JOIN user_love ul 
     ON ul.to_user_id = user.id 
    AND ul.from_user_id = '$user_id' 

    ORDER BY sr.id DESC 
    LIMIT 30 

ПРИМЕЧАНИЕ: (., Как первоначальный запрос)

Рядный вид sr предназначен, чтобы получить максимум 30 строк из friend_request, упорядоченных по id убыванию

Похоже, что исходный запрос является намереваясь найти строки, где указанный $user_id находится в столбце from_ или to_.

Старые версии MySQL могут создавать довольно неприятные планы выполнения для запросов с использованием предикатов OR для операций JOIN. Обычным обходным путем для этого является использование операции UNION ALL или UNION возврата из двух отдельных операторов SELECT ... каждый SELECT может быть оптимизирован для использования соответствующего индекса.

Как только у нас есть этот набор результатов от sr, остальная часть запроса довольно проста.

+0

Вау! Большое спасибо. У меня просто есть синтаксическая ошибка SQL с вашим запросом, и я не могу найти, откуда она находится: '# 1064 - У вас есть ошибка в синтаксисе SQL; проверьте руководство, соответствующее версии вашего сервера MySQL, для правильного синтаксиса для использования рядом с «AS age FROM» ((SELECT frf.id, frf.to_user_id AS user_id FROM friend_request »в строке 11' – fraxool

+0

Ooops. 'AS age'. – spencer7593

+0

Только последний!' # 1054 - Неизвестный столбец 'fr.user_id' в 'on clause''. Вы можете помочь? Большое спасибо! – fraxool

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