如何在多列上使用DISTINCT?(消息系统场景咨询)
嘿,针对你设计的这个消息系统(父消息带标题且related为NULL,子消息无标题靠related关联父ID),我结合你的表结构来拆解多列DISTINCT的用法,都是实际开发里常用的场景:
1. 基础多列DISTINCT用法
首先得明确:DISTINCT是对你指定的所有列的组合去重,不是单独给每一列去重。举个最简单的语法例子:
SELECT DISTINCT column1, column2, ... FROM messages;
比如你想拿到所有不重复的「发送者-接收者-关联父消息」组合,就可以这么写:
SELECT DISTINCT sender_id, receiver_id, related FROM messages;
这会自动过滤掉这三列完全一模一样的重复行。
2. 贴合你消息场景的实用写法
场景A:按对话维度获取唯一消息组
如果你想按「发送者+接收者」的对话维度,拿到每个对话里不重复的消息组(父消息+对应的子消息算一组),可以用COALESCE把父消息的ID和子消息的关联父ID统一起来,再结合DISTINCT或者GROUP BY去重:
SELECT DISTINCT sender_id, receiver_id, COALESCE(related, id) AS parent_message_id, MAX(title) AS message_title -- 子消息没标题,用MAX直接取父消息的标题就行 FROM messages GROUP BY sender_id, receiver_id, COALESCE(related, id);
这样每个对话下的同组消息只会显示一条,还能带上父消息的标题。
场景B:彻底去掉表中重复的消息记录
如果你的表不小心插入了完全重复的消息(所有列都一样),想保留唯一的行,直接把所有列放到DISTINCT后面就行:
SELECT DISTINCT id, title, content, sender_id, receiver_id, date_time, related FROM messages;
这会返回所有列组合唯一的消息,完全清除重复记录。
场景C:按特定列去重,保留最新/指定行
如果你只想让某一列(比如related)唯一,每个related值只留一条最新的消息,MySQL 8.0+可以用窗口函数实现(因为MySQL不支持PostgreSQL的DISTINCT ON):
SELECT * FROM ( SELECT *, ROW_NUMBER() OVER (PARTITION BY related ORDER BY date_time DESC) AS rn FROM messages ) t WHERE rn = 1;
这个查询会按related分组,每个组里取时间最新的那条消息,达到“按related去重”的效果。
关键提醒
别搞混了:DISTINCT是对所有选中列的组合去重,不是分别给每一列去重。比如SELECT DISTINCT a, b不会把a列的重复值去掉、再把b列的重复值去掉,而是去掉(a,b)组合完全相同的行。如果需要分组聚合,用GROUP BY会比DISTINCT更直观哦。
内容的提问来源于stack exchange,提问作者Martin AJ

