
本文教你通过优化数据库查询与结构,实现类似 messenger 的“最近联系人”功能——仅显示与当前用户有收发消息记录的用户,并按最新消息时间倒序排列。
本文教你通过优化数据库查询与结构,实现类似 messenger 的“最近联系人”功能——仅显示与当前用户有收发消息记录的用户,并按最新消息时间倒序排列。
在构建实时聊天应用时,简单地列出所有注册用户(如 SELECT * FROM users WHERE unique_id != ?)无法体现真实对话关系,也违背用户体验逻辑。理想方案是:只展示与当前用户存在消息往来(发送或接收过消息)的联系人,并按最后一条交互消息的时间从新到旧排序。
为此,需从三方面重构:
1. 优化表结构(关键前提)
首先修正 messages 表字段语义与数据类型:
- 将 incoming_msg_id 改为 receiver_id(INT),outgoing_msg_id 改为 sender_id(INT);
- 将 msg_time 字段类型由 VARCHAR 升级为 TIMESTAMP(推荐使用 DATETIME 或 TIMESTAMP,支持精确排序与索引优化);
ALTER TABLE messages CHANGE incoming_msg_id receiver_id INT NOT NULL, CHANGE outgoing_msg_id sender_id INT NOT NULL, MODIFY msg_time DATETIME DEFAULT CURRENT_TIMESTAMP;
✅ 提示:确保 receiver_id 和 sender_id 均建立索引(如 INDEX(receiver_id), INDEX(sender_id)),大幅提升后续 JOIN 查询性能。
2. 构建动态联系人查询
核心逻辑:找出所有曾与当前用户($_SESSION['unique_id'])发生过单向或双向消息交互的用户,并取其最新一条消息时间作为排序依据。
推荐使用 UNION 合并「我发给对方」和「对方发给我」两类记录,再通过子查询去重并排序:
$my_id = (int)$_SESSION['unique_id'];
$sql = "
SELECT DISTINCT u.*,
(SELECT MAX(m.msg_time)
FROM messages m
WHERE (m.sender_id = $my_id AND m.receiver_id = u.unique_id)
OR (m.receiver_id = $my_id AND m.sender_id = u.unique_id)
) AS last_msg_time
FROM users u
INNER JOIN messages m ON (
m.sender_id = $my_id AND m.receiver_id = u.unique_id
OR
m.receiver_id = $my_id AND m.sender_id = u.unique_id
)
WHERE u.unique_id != $my_id
ORDER BY last_msg_time DESC
";
$result = mysqli_query($conn, $sql);
该查询返回每位联系人的完整用户信息(u.*)及双方最后互动时间(last_msg_time),天然支持顶部置顶最新聊天对象。
3. 前端渲染建议(AJAX 场景)
在 AJAX 成功回调中,遍历结果并动态插入 DOM,例如:
// 假设返回 JSON 格式:[{unique_id:123, name:"Alice", last_msg_time:"2024-05-20 14:30:00"}, ...]
data.forEach(user => {
const el = `<div class="contact" data-id="${user.unique_id}">
<span>${user.name}</span>
<small>${formatRelativeTime(user.last_msg_time)}</small>
</div>`;
$('#contacts-list').append(el);
});
⚠️ 安全提醒:务必对 $my_id 进行 (int) 强制类型转换或使用预处理语句(mysqli_prepare),杜绝 SQL 注入风险。
综上,真正的“消息驱动联系人列表”依赖于语义清晰的表结构、精准的关联查询与合理的时间排序。避免全量用户扫描,聚焦消息图谱,才能让聊天界面既高效又符合直觉。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











