有没有更简单的方法来实现这种用户消息传递方式?

Dan*_*Dan 28 mysql sql database database-design innodb

我为用户创建了一个消息传递系统,它允许他们向另一个用户发送消息.如果这是他们第一次说话,那么就会启动新的会话,如果不是旧的会话继续.

用户收件箱列出了用户与所有其他用户进行的所有对话,然后由具有最新帖子的对话对这些对话进行排序.

用户只能与另一个用户进行一次对话.

当用户点击其中一个对话时,他们会被带到一个页面,显示他们在顶部有最新帖子的整个对话.所以它有点像消息聊天功能.

我有两张桌子:

  • userconversation
  • usermessages

userconversation

包含一个自动增量ID,它是会话ID,以及userId和friendId.

无论是谁发起第一个对话都将是userId和收件人friendId,这将永远不会改变该对话.

+----+--------+----------+
| id | userId | friendId |
+----+--------+----------+
Run Code Online (Sandbox Code Playgroud)

usermessages

包含特定消息,以及读取标志,时间和conversationId

+----+---------+--------+------+------+----------------+
| id | message | userId | read | time | conversationId |
+----+---------+--------+------+------+----------------+
Run Code Online (Sandbox Code Playgroud)

这个怎么运作

当用户转到另一个用户的消息时,将运行查询以检查两个用户是否在userconversation表中匹配,如果是,conversationId则使用该会话并且会话继续进行,如果不是,则为他们创建具有唯一的新行conversationId.

它变得复杂的地方

到目前为止一切都很好,但是当涉及到显示所有对话的消息收件箱时,按照最新的帖子进行排序,一个查询就变得棘手了.

为了能够列出对话,您必须首先找到每个对话的最新帖子,但由于您无法在组之前订购,因此无法对两个表上的一个查询进行排序,因此我必须使用以下内容:

SELECT  
    c.id,
    c.userId,
    c.friendId,
    m2.message,
    m2.read,
    UNIX_TIMESTAMP(m2.time),      
    user1.username,
    user2.username  
FROM 
    (SELECT MAX(m1.id) AS MessageID 
     FROM usermessages m1 
     GROUP BY m1.conversationId) latest_msg

INNER JOIN usermessages m2 ON latest_msg.MessageID = m2.id 
INNER JOIN userconversation c ON m2.conversationId = c.id
INNER JOIN user user1 ON c.userId = user.id
INNER JOIN user user2 ON c.friendId = user.id

WHERE c.userId = :userId OR c.friendId = :userId
ORDER BY m2.id DESC
LIMIT 10
Run Code Online (Sandbox Code Playgroud)

我只是觉得这不是最好的方式,但是不能想到别人的方法呢?

数据库表是InnoDB,用于加速连接并提高数据完整性,因此我不能有两个自动增量行.

有没有其他方法可以摆脱userconversation表并创建一个唯一的ID放在conversationId列?然后,我可以将userId和friendId移动到usermessages ...但这会产生大量冗余数据?

ple*_*ong 6

嗯,也许我不能正确理解你的问题...但对我来说解决方案很简单:

SELECT c.*, MAX(m.time) as latest_post 
FROM conversations as c 
INNER JOIN messages as m ON c.id = m.conversation_id
WHERE c.userId = 222 OR c.friendId = 222 
GROUP BY c.id
ORDER BY latest_post DESC
Run Code Online (Sandbox Code Playgroud)

这是我的测试数据:

对话:

id  userId  friendId
1   222     333
2   222     444
Run Code Online (Sandbox Code Playgroud)

消息:

id  message     time (Desc)     conversation_id
14  rty     2012-05-14 19:59:55     2
13  cvb     2012-05-14 19:59:51     1
12  dfg     2012-05-14 19:59:46     2
11  ert     2012-05-14 19:59:42     1
1   foo     2012-05-14 19:22:57     2
2   bar     2012-05-14 19:22:57     2
3   foo     2012-05-14 19:14:13     1
8   wer     2012-05-13 19:59:37     2
9   sdf     2012-05-13 19:59:24     1
10  xcv     2012-05-11 19:59:32     2
4   bar     2012-05-10 19:58:06     1
6   zxc     2012-05-08 19:59:17     2
5   asd     2012-05-08 19:58:56     1
7   qwe     2012-05-04 19:59:20     1
Run Code Online (Sandbox Code Playgroud)

查询结果:

id  userId  friendId    latest_post
2   222     444     2012-05-14 19:59:55
1   222     333     2012-05-14 19:59:51
Run Code Online (Sandbox Code Playgroud)

如果那不是它......只是忽略我的答案:P

希望这可以帮助


Jef*_*ert 4

如果您正在寻求一种能够保留所有当前功能和工作流程,同时将数据保留在单个表中的方法,我认为您已经非常接近了。

我不想让它成为conversationId不同表的键,而是让它指向开始对话的消息的 ID。这将在开始对话的消息和对话之后的所有消息之间创建父子关系。为了能够查看所有对话,您只需选择所有为conversationId空的消息。下面是 2 条消息对话的表示:

+----+---------+------+------------------+----------------+--------+----------+
| id | message | read | time             | conversationId | toUser | fromUser |
+----+---------+------+------------------+----------------+--------+----------+
| 1  |  test 1 |  0   | (some timestamp) |  null          |  3     |   4      |
| 2  |  test 2 |  0   | (some timestamp) |   1            |  4     |   3      |
+----+---------+------+------------------+----------------+--------+----------+
Run Code Online (Sandbox Code Playgroud)

该对话由用户 3 发起。对话中的所有消息都可以通过 进行过滤conversationId。此设计的一个限制是只能有 2 个用户参与对话。

更新

您可以通过以下方式获取给定对话 ID 的最后一条消息:

SELECT id, message 
FROM userMessages 
WHERE conversationId = {conversationId} 
ORDER BY time DESC 
LIMIT 1
Run Code Online (Sandbox Code Playgroud)