Is there a simpler way to achieve this style of user messaging?

前端 未结 13 1244
轮回少年
轮回少年 2020-12-24 12:20

I have created a messaging system for users, it allows them to send a message to another user. If it is the first time they have spoken then a new conversation is initiated,

13条回答
  •  自闭症患者
    2020-12-24 12:55

    Since a given pair of users can have at most one conversation, there is no need to "invent" separate key just to identify conversations. Also, the wording of your question seems to suggest that a message is always sent to a single user, so I'd probably go with something like this:

    enter image description here

    Now, there are several things to note about this model:

    • It assumes messages between same two users cannot be generated more frequently than the resolution provided by the type used for SEND_TIME.1
    • The direction of the message is not determined by order of USER1_ID and USER2_ID, but with a separate flag (DIRECTION). This way, a message between given users will always have the same combination of USER1_ID and USER2_ID (enforced by the CHECK above), regardless of who sent and who received the message. This greatly simplifies querying.
    • It is unfortunate that all InnoDB tables are clustered, so the secondary index I1 is relatively expensive. There are ways to work around that, but the resulting complications are probably not worth it.

    With this data model, it becomes rather easy to sort the "conversations" (identified by user pairs) by the latest message. For example (replace 1 with desired user's USER_ID):

    SELECT *
    FROM (
        SELECT USER1_ID, USER2_ID, MAX(SEND_TIME) NEWEST
        FROM MESSAGE
        WHERE (USER1_ID = 1 OR USER2_ID = 1)
        GROUP BY USER1_ID, USER2_ID
    ) Q
    ORDER BY NEWEST DESC;
    

    (OR USER2_ID = 1 is the reason for the secondary index I1.)

    If you want not just latest times, but also latest messages, you can do something like this:

    SELECT * FROM MESSAGE T1
    WHERE
        (USER1_ID = 1 OR USER2_ID = 1)
        AND SEND_TIME = (
            SELECT MAX(SEND_TIME)
            FROM MESSAGE T2
            WHERE
                T1.USER1_ID = T2.USER1_ID
                AND T1.USER2_ID = T2.USER2_ID
        )
    ORDER BY SEND_TIME DESC;
    

    You can play with it in the SQL Fiddle.


    1 If that's not the case, you can use monotonically-incrementing INT instead, but you'll have to SELECT MAX(...) yourself since auto-increment doesn't work on PK subset; or simply make it PK alone and have secondary indexes on both USER1_ID and USER2_ID (fortunately, they would be slimmer since the PK is slimmer).

提交回复
热议问题