How to improve my friend list MySQL query?

时间:2015-06-15 15:17:18

标签: mysql sql

I have a big MySQL query which actually returns the good set of result but it is quite slow.

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" is the id of the logged-in user.

Here is the table structure of "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 ;

Can you help me to improve this query?

I have not copied the table structure of the other tables because, after some tests, the "optimization issue" seems to come from the friend_request table.

Thanks!

EDIT :

Here is what "EXPLAIN" gives me :

enter image description here

2 个答案:

答案 0 :(得分:1)

您应该查看查询计划或尝试使用关键字Explain https://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

编辑:格式化,发现照片表连接对于正在选择的数据是必要的

答案 1 :(得分:0)

我会像这样编写查询。 (我还会在查询中对所有列的引用进行限定,usernameemaillast_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旨在从friend_request获取最多30行,按id降序排序(与原始查询一样)。

原始查询似乎打算在$user_idfrom_列中找到指定to_的行。

旧版本的MySQL可以为涉及JOIN操作的OR谓词的查询生成一些非常令人讨厌的执行计划。通常的解决方法是使用两个单独的SELECT语句返回的UNION ALLUNION操作...可以优化每个SELECT以使用适当的索引。

sr获得结果集后,查询的其余部分非常简单。