Laravel 8中如何按from_user_id和to_user_id分组获取唯一会话?
方案一:无需修改表结构,查询时生成统一会话标识
核心逻辑是通过数据库内置函数将两个用户ID按固定顺序排序,生成同一对话的唯一标识,避免收发顺序不同导致分组拆分。
大多数关系型数据库都支持LEAST()(取多个值的最小值)和GREATEST()(取多个值的最大值)函数,不管消息收发方向如何,同一对用户生成的两个值组合完全一致。
对应的Laravel查询构造器写法如下:
->selectRaw('LEAST(from_user_id, to_user_id) as session_user1, GREATEST(from_user_id, to_user_id) as session_user2, MAX(created_at) as latest_msg_time, id, content') ->where(function($q) use($user_id) { $q->where('from_user_id', $user_id) ->orWhere('to_user_id', $user_id); }) ->groupBy('session_user1', 'session_user2') ->orderBy('latest_msg_time', 'desc');
注意:如果数据库开启了严格模式,需要调整select的字段规则,或者先通过子查询按消息时间倒序排列后再分组,确保拿到的是每个会话最新的消息。
该方案适配快速上线需求,缺点是分组时需要做字段计算,无法走索引,数据量超过十万级后查询性能会明显下降。
方案二:调整为会话+消息的二级表结构(推荐长期使用)
这是聊天系统的标准设计,从表结构层面解决会话唯一的问题,后续扩展也更灵活:
- 新增
conversation会话表,核心字段:- id 主键
- user_min_id 两个用户ID的较小值
- user_max_id 两个用户ID的较大值
- latest_msg 最新消息预览
- latest_msg_at 最新消息时间
- unread_count 未读消息数(可按用户维度拆分存储)
- 可扩展置顶、免打扰等会话属性
- 原有消息表改为
conversation_message,新增conversation_id关联会话表,保留原有收发人、内容、时间等字段。
新增消息时先查询对应会话是否存在,不存在则创建,存在则更新会话的最新消息、时间等字段。查询会话列表时直接从conversation表筛选包含当前登录用户的记录即可,不需要分组计算,性能远高于单表方案,也不会出现会话重复的问题。
内容的提问来源于stack exchange,提问作者Devsi Odedra
相关产品推荐
相关产品推荐

