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 :
答案 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)
我会像这样编写查询。 (我还会在查询中对所有列的引用进行限定,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
旨在从friend_request获取最多30行,按id
降序排序(与原始查询一样)。
原始查询似乎打算在$user_id
或from_
列中找到指定to_
的行。
旧版本的MySQL可以为涉及JOIN操作的OR
谓词的查询生成一些非常令人讨厌的执行计划。通常的解决方法是使用两个单独的SELECT语句返回的UNION ALL
或UNION
操作...可以优化每个SELECT以使用适当的索引。
从sr
获得结果集后,查询的其余部分非常简单。