如何加快这个 MySQL 查询?最新消息查询

Posted

技术标签:

【中文标题】如何加快这个 MySQL 查询?最新消息查询【英文标题】:How to speed up this MySQL query? Latest messages query 【发布时间】:2014-10-30 10:00:56 【问题描述】:

我需要加快此查询以列出最新消息。此查询运行时间过长(例如 10 秒 ...)

SELECT datas.uid,
       datas.message,
       datas.date,
       CONCAT(conv.first_name, ' ', conv.last_name) AS conversation_name
FROM   (SELECT m.message_id,
               m.message,
               IF (m.from_uid = 1, m.to_uid, m.from_uid) AS uid,
               m.readed,
               m.sended                                  AS `date`
        FROM   users u
               LEFT JOIN messages m
                      ON m.from_uid = u.user_id
        WHERE  m.message_id IN (SELECT MAX(message_id)
                                FROM   messages
                                WHERE  to_uid = 1
                                        OR from_uid = 1
                                GROUP  BY LEAST(from_uid, to_uid),
                                          GREATEST(from_uid, to_uid))) datas
       LEFT JOIN users conv
              ON conv.user_id = datas.uid
ORDER  BY datas.date DESC
LIMIT  5 

此查询使用 2 个表(用户和消息)。 表用户:

user_id(主要,自动增量) 登录 通过 名字 姓氏 ....

表格消息:

message_id(主要,自动增量) from_uid (sender message, reference to table users -> user_id) to_uid (receiver message, reference to table users -> user_id) 已发送(时间戳) 消息(varchar)

编辑 我为消息添加了索引: - from_uid - to_uid - 发送

这是没有效果的......

【问题讨论】:

你的表有什么索引? 我没有索引...只有主键 所以你希望人们分析你的查询来得到你想要做什么?请描述您的目标并添加示例数据和预期输出。您正在创建 2 个子选择 - 对性能没有好处。 我需要这样的结果: - uid(带有 user_id 会话) - message(会话中的最新消息) - conversation_name(带有用户名和姓氏会话) - 日期(会话时间戳中的最新消息) 我在消息中添加了索引:from_uid,to_uid,send,这没有效果 【参考方案1】:

尝试在您正在检查的 id 上创建索引。

在这种情况下,您可能希望在以下位置创建索引:conv.user_id、datas.uid、m.message_id、messages.to_uid、messages.from_uid 和 datas.date,因为您'重新排序。

【讨论】:

【参考方案2】:

from_uidto_uid 上添加索引以加快SELECT MAX(message_id) 子查询。否则,它必须对表进行全扫描。

【讨论】:

【参考方案3】:

我会尝试删除子查询

当从 te 和 uid 获取用户详细信息时,您可以通过加入两次用户来清理它。这样连接可以有效地使用索引。然后只需在 SELECT 中使用 IF 来决定返回哪个:-

SELECT IF (m.from_uid = 1, m.to_uid, m.from_uid) AS uid,
        m.message,
        m.sended AS `date`,
        IF (m.from_uid = 1, CONCAT(to_conv.first_name, ' ', to_conv.last_name), CONCAT(from_conv.first_name, ' ', from_conv.last_name)) AS conversation_name
FROM   users u
LEFT JOIN messages m
ON m.from_uid = u.user_id
LEFT JOIN users to_conv
ON to_conv.user_id = m.to_uid
LEFT JOIN users from_conv
ON from_conv.user_id = m.from_uid
WHERE  m.message_id IN 
(
    SELECT MAX(message_id)
    FROM   messages
    WHERE  to_uid = 1
    OR from_uid = 1
    GROUP  BY LEAST(from_uid, to_uid),
    GREATEST(from_uid, to_uid)
)
ORDER  BY date DESC
LIMIT  5 

检查消息 ID 的子查询有点难以删除。通常 IN 表现不佳。可能值得将其更改为使用 EXISTS(尽管这将需要检查 HAVING 子句,这可能不好)

SELECT IF (m.from_uid = 1, m.to_uid, m.from_uid) AS uid,
        m.message,
        m.sended AS `date`,
        IF (m.from_uid = 1, CONCAT(to_conv.first_name, ' ', to_conv.last_name), CONCAT(from_conv.first_name, ' ', from_conv.last_name)) AS conversation_name
FROM   users u
LEFT JOIN messages m
ON m.from_uid = u.user_id
LEFT JOIN users to_conv
ON to_conv.user_id = m.to_uid
LEFT JOIN users from_conv
ON from_conv.user_id = m.from_uid
WHERE  EXISTS 
(
    SELECT MAX(message_id) AS max_message_id
    FROM   messages
    WHERE  to_uid = 1
    OR from_uid = 1
    GROUP  BY LEAST(from_uid, to_uid), GREATEST(from_uid, to_uid)
    HAVING m.message_id = max_message_id
)
ORDER  BY date DESC
LIMIT  5 

【讨论】:

您好,感谢您的回复...我正在尝试此查询但没有效果...我将 INDEXes 添加到 messages.from_uid 和 messages.to_uid 与 users.user_id 的 FOREIGN KEY 并且也没有效果: ( 你能发布实际的表格声明和一些测试数据吗?然后我可以尝试运行它(虽然明天我现在才有机会)

以上是关于如何加快这个 MySQL 查询?最新消息查询的主要内容,如果未能解决你的问题,请参考以下文章

如何加快这个 MySQL 查询

Mysql 分组查询,并且返回最新的一条数据如何实现

MYSQL 查询 - 获取与帖子相关的最新评论

我的子查询将执行时间增加了 20 秒。我怎样才能加快速度?

mysql 7 +加入一个表,如何加快速度?

php+mysql 如何优化千万级数据模糊查询加快