我正试图为用户制作收件箱。我需要显示按通讯员分组的所有线程,并按照特定通信的最后发布消息的时间排序。 我坚持使用这个sql并且不知道我该怎么做:
CREATE TABLE `user_mail` (
`id` int(10) NOT NULL,
`author` int(10) NOT NULL,
`recipient` int(10) NOT NULL,
`title` varchar(100) NOT NULL,
`message` text NOT NULL,
`date` int(100) NOT NULL,
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
SELECT * FROM user_mail t1
INNER JOIN
(SELECT author, recepient, MAX(date) AS Ordered FROM user_mail
WHERE recepient = '$thisUser' OR author = '$thisUser' GROUP BY author) t2
ON t1.author = t2.author
WHERE t1.recepient = '$thisUser' OR t1.author = '$thisUser'
ORDER BY t2.Ordered DESC
这是我需要展示的方案:
Correspondence with User 1
Newest reply - author: User 1 | time: 11:00
Next reply - author: This user | time: ...
Reply - author: User 1 | time: ...
...
Original post - author: This user | time: 09:30
________________________________________________
Correspondence with User 2
Newest reply - author: This user | time: 10:30
...
Original post - author: User 2 | time: 10:00
你可以看到与用户1的通信如何处于最佳位置,因为它有最新的回复(虽然它的原始帖子比另一个更旧)。
此外,应显示所有对应关系,无论是用户启动了还是另一个用户启动了它们。
答案 0 :(得分:1)
使用以下SQL语句,结果将与您的显示示例相同。
SELECT id
,CASE WHEN rn_min = 1
THEN 'Original Post - '
WHEN rn_max = 1
THEN 'Newest reply - '
WHEN rn_min = 2 AND rn_max != 2
THEN 'Reply - '
ELSE 'Next reply - '
END +
CASE WHEN author = @thisuser
THEN 'author: This ' + CONVERT(VARCHAR, author)
ELSE 'author: User ' + CONVERT(VARCHAR, author)
END +
CASE WHEN rn_min = 1 OR rn_max = 1
THEN ' | time: '+ CONVERT(VARCHAR(8),posteddate,108)
ELSE ''
END value
FROM (SELECT id
,author
,recipient
,message
,posteddate
,row_number() OVER (PARTITION BY id ORDER BY posteddate) rn_min
,row_number() OVER (PARTITION BY id ORDER BY posteddate desc) rn_max
FROM user_mail
WHERE author = @thisuser OR recipient = @thisuser
) t1
答案 1 :(得分:1)
由于您的用户可以同时位于两个列中,因此您必须使用搜索中的两列值和您的组。
试试这个:
select *
from user_mail t1
join
(
select max(date) as ConvMaxDate,
case when author = '$thisUser' then recipient
else author
end as OtherUser
from user_mail
where author = '$thisUser' or recipient = '$thisUser'
group by case when author = '$thisUser' then recipient
else author
end
) ConversationMaxDate
on Author = '$thisUser' and OtherUser = recipient
or Recipient = '$thisUser' and OtherUser = Author
order by ConvMaxDate desc, Date desc;
ConversationMaxDate
的内部查询首先确定会话伙伴,然后通过此“OtherUser”进行分组,计算每个主题的最新日期。这是有效的,因为您可以提供“ThisUser”(因为只有这样您才能在特定的电子邮件中知道哪一个是对话中的那个。)
您需要(author, recipient, date)
和(recipient, author, date)
上的索引,因为MySQL可以使用索引合并。否则,它将需要完整的表/索引扫描。
答案 2 :(得分:1)
由于$thisUser
是author
或recipient
,您不知道每条消息,您可以使用LEAST(author, recipient)
和GREATEST(author, recipient)
来识别"线程"并在子查询的GROUP BY子句和JOIN条件中使用它们。
SELECT m.*
FROM user_mail m
JOIN (
SELECT
LEAST(author, recipient) as user1,
GREATEST(author, recipient) as user2,
MAX(date) as date
FROM user_mail
WHERE $thisUser IN (author, recipient)
GROUP BY user1, user2
) s ON s.user1 = LEAST(m.author, m.recipient)
AND s.user2 = GREATEST(m.author, m.recipient)
WHERE $thisUser IN (m.author, m.recipient)
ORDER BY
s.date DESC,
LEAST(m.author, m.recipient),
GREATEST(m.author, m.recipient),
m.date DESC
但是对于大数据集来说这会很慢,因为没有索引可以用于GROUP BY子句和JOIN条件。
我会制作id
AUTO_INCREMENT PRIMARY KEY
并使用它代替date
。
这样,您至少可以为JOIN使用索引(PK)。查询也会缩短。
SELECT m.*
FROM user_mail m
JOIN (
SELECT MAX(id) as id
FROM user_mail
WHERE $thisUser IN (author, recipient)
GROUP BY
LEAST(author, recipient),
GREATEST(author, recipient)
) s ON s.id = m.id
ORDER BY s.id DESC, m.id DESC
使用针对子查询的UNION ALL优化,您可以获得更好的性能。
SELECT m.*
FROM user_mail m
JOIN (
SELECT MAX(id) as id
FROM (
SELECT recipient as user, MAX(id) as id
FROM user_mail
WHERE author = $thisUser
GROUP BY recipient
UNION ALL
SELECT author as user, MAX(id) as id
FROM user_mail
WHERE recipient = $thisUser
GROUP BY author
) sub1
GROUP BY user
) s ON s.id = m.id
ORDER BY s.id DESC, m.id DESC
对于此查询,您应该在(author, recipient)
和(recipient, author)
上定义复合索引。
您的评论是正确的:最后两个查询仅返回每个对话的最新消息。但第一个应该返回所有消息。
但是 - 这是UNION ALL优化查询的正确版本:
SELECT m.*, s.max_id
FROM user_mail m
JOIN (
SELECT other_user, MAX(id) as max_id
FROM (
SELECT recipient as other_user, MAX(id) as id
FROM user_mail
WHERE author = $thisUser
GROUP BY recipient
UNION ALL
SELECT author as other_user, MAX(id) as id
FROM user_mail
WHERE recipient = $thisUser
GROUP BY author
) sub1
GROUP BY other_user
) s ON s.other_user = m.recipient
WHERE m.author = $thisUser
UNION ALL
SELECT m.*, s.max_id
FROM user_mail m
JOIN (
SELECT other_user, MAX(id) as max_id
FROM (
SELECT recipient as other_user, MAX(id) as id
FROM user_mail
WHERE author = $thisUser
GROUP BY recipient
UNION ALL
SELECT author as other_user, MAX(id) as id
FROM user_mail
WHERE recipient = $thisUser
GROUP BY author
) sub1
GROUP BY other_user
) s ON s.other_user = m.author
WHERE m.recipient = $thisUser
ORDER BY max_id DESC, id DESC
虽然看起来很大,但这个查询在我的百万行测试数据集上的运行时间不到20毫秒(而其他解决方案需要300 - 500毫秒)。 请注意,子查询在两个部分中都是相同的。 MySQL应该能够缓存并重用结果。 为避免代码重复,您可以将子查询存储在字符串变量中并重用它。如果您使用MariaDB 10.2,您可能还想尝试CTE。
也不要忘记在(author, recipient)
和(recipient, author)
上定义索引