我正在编写一个私人会话系统,需要就我最复杂的请求提出建议。
我的(简化)表,在MySQL 5.5下:
user:
id (int)
username (string)
conversation:
id (int)
subject (string)
conversation_user:
conversation_id (int)
user_id (int)
message:
id (int)
conversation_id (int)
user_id (int)
content (text)
created_at (datetime)
所以,我想向用户显示他参与的对话列表:对于每个对话,我需要对话ID,对话主题,参与用户列表,最后一个消息ID,最后一个消息作者ID和用户名,以及最后一条消息日期。
我写了这个请求,看起来很糟糕:
SELECT
cu.conversation_id,
c.subject,
u.username,
m.id,
m.user_id,
m.created_at
FROM conversation_user cu
JOIN conversation c ON c.id = cu.conversation_id
JOIN conversation_user cu2 ON cu2.conversation_id = c.conversation_id
JOIN user u ON u.id = cu2.user_id
JOIN message m ON m.conversation_id = cu.conversation_id
WHERE cu.user_id = :current_user_id # the session user_id
AND m.id = (SELECT MAX(id) FROM message WHERE conversation_id = cu.conversation_id)
所以,我想知道你们是否看到了更好的方法来获得相同的结果?
我已经阅读了GROUP BY behavior when no aggregate functions are present in the SELECT clause,这就是为什么我没有放置GROUP BY子句并写下最后一行:
AND m.id = (SELECT MAX(id) FROM message WHERE conversation_id = cu.conversation_id)
谢谢!
我对此请求做了一个EXPLAIN,并且没有用于JOIN user u ON u.id = cu2.user_id
行的KEY:为什么?
(第一个问题仍然相关)
答案 0 :(得分:1)
还有其他选择,例如我即将为您提出的。但是,说实话,我就是这样做的。)
AND m.id = (SELECT id FROM message WHERE conversation_id = cu.conversation_id ORDER BY id DESC LIMIT 1)
以上允许您按日期或其他字段订购,并且仍然只选择一个消息ID。
JOIN
message m
ON m.conversation_id = cu.conversation_id
JOIN
(SELECT conversation_id, MAX(id) AS id FROM message GROUP BY conversation_id) AS filter
ON filter.conversation_id = cu.conversation_id
AND filter.id = m.id
以上内容避免了相关的子查询,因此可以(但并非总是)更快。
就JOIN user u ON u.id = cu2.user_id
没有使用密钥而言,这两个表中的两个在相关字段上都有索引吗?