MYSQL查询优化联接4个表

时间:2019-05-10 09:01:19

标签: mysql optimization

需要帮助来优化此查询

任务是从friends表中选择我的朋友,检查他们中是否有任何一个都在忽略表中,从用户表中获取他们的信息,以及从消息表中获取最后一个消息日期。我的朋友也可以是团体的。

我的查询:

SELECT
        `u`.`id`, `u`.`login`, `u`.`name`, `u`.`surname`,
        CONCAT(`u`.`name`, ' ', `u`.`surname`) AS `namesurname`, `u`.`avatar`,
        `u`.`status`, `u`.`status_text`, `u`.`last_active`, `u`.`type`,
        `f`.`status` AS `friend_state`, `i`.`date` AS `ignore_date`,
        MAX(`m`.`date`) AS `last_message_date` 
    FROM `friends` AS f 
    INNER JOIN `users` AS u ON `u`.`id`=`f`.`friend_id`
    LEFT JOIN `ignore` AS `i` ON `i`.`uid`='10' AND `i`.`ignore_id`=`f`.`friend_id`
    LEFT JOIN `messages` AS `m` ON `m`.`date`>DATE_ADD(NOW(), INTERVAL -1 DAY) AND 
    (
        (`m`.`fromid`=`u`.`id` AND `m`.`toid`='10') OR 
        (`m`.`toid`=`u`.`id` AND `m`.`fromid`='10') OR
        (`m`.`toid`=`u`.`id` AND `m`.`toid` IN (16,22,27))
    ) 
    WHERE `f`.`uid`='10'
    GROUP BY `u`.`id`
    ORDER BY `last_message_date` DESC, `namesurname`

对于170k条消息,此查询耗时2.2-2.5秒。

如果我删除

(`m`.`toid`=`u`.`id` AND `m`.`toid` IN (16,22,27))

花费了0.55秒

10-是我的用户ID

16,22,27-群组ID

我需要这一行(mtoid = uidmtoid IN(16,22,27 )),因为在群聊中,它不应该取决于发送最后一条消息的人。

预先感谢

1 个答案:

答案 0 :(得分:0)

您可能不需要LEFT。假设,我将ORs变成UNIONs并使用以下命令 start

由于您尚未提供SHOW CREATE TABLE,因此我猜想messagesPRIMARY KEY(mid)

SELECT ...
    FROM ( ( SELECT mid FROM messages WHERE fromid = u.id AND toid = 10 )
           UNION ALL 
           ( SELECT mid FROM messages WHERE toid = u.id AND fromid = 10 )
           UNION ALL 
           ( SELECT mid FROM messages WHERE toid = u.id AND toid IN (16,22,27) ) AS um
    JOIN users AS u  ON ...
    JOIN 

糟糕,我将首先尝试在messages中尝试做尽可能多的事情,假设1天的限制是一个重要的过滤条件。 (请注意,估计需要扫描17万行。)

首先检查一下它是否“快速”,并且不传递“太多”的行:

SELECT COUNT(*) FROM (
        ( SELECT toid AS either_id, MAX(date) AS last_date FROM messages
             WHERE toid IN (10, 16,22,27) AND date > NOW() + INTERVAL 1 DAY )
        UNION ALL   -- or, if you get dups, UNION ALL
        ( SELECT fromid, MAX(date) FROM messages
             WHERE fromid = 10 AND date > NOW() + INTERVAL 1 DAY )
                     ) um;

假设没问题,然后继续工作

SELECT ...
    FROM ( ... ) AS um   -- the derived table above
    JOIN users AS u  ON um.either_id = u.id
    JOIN ...   -- the rest of the stuff

索引...

messages:  (toid, date)
messages:  (fromid, date)

(这是一个非常混乱的查询,因此我对自己的帮助没有信心。)