在 SQL 中选择发送者和接收者之间的最后一条消息

Posted

技术标签:

【中文标题】在 SQL 中选择发送者和接收者之间的最后一条消息【英文标题】:Select last message between a sender and a receiver in SQL 【发布时间】:2021-01-13 14:10:55 【问题描述】:

我目前正在尝试获取数据库中的最后一条消息,以便创建与其他消息系统(如 Facebook Messenger 或 WhatsApp)类似的系统,但我想获取发件人“对话”的最后一条消息ID (id_emmeteur_id) 和接收者 ID (id_destinataire_id)。

问题是我只成功获取了所有消息,并且我想根据连接的用户只获取两个用户之间的最后一条消息(连接的一个和另一个,取决于我的 ID在我的消息表中有)。

我将我的“消息”表结构与一些示例放在下面,希望对您有所帮助。

id|id_emmeteur_id|id_destinataire_id|contenu
--------------------------------------------
 1|             3|                 1|test
 2|             3|                 1|another test
 3|             1|                 3|test number 3
 4|             1|                 2|another user

通过这些示例,从逻辑上讲,我应该(如果连接的用户的 ID 为“1”)消息“test number 3”和“another user”,因为 ID 为“1”和 ID 的用户之间的最后一条消息“3”是“3 号测试”,因为“另一个用户”是 ID 为“1”和 ID 为“2”的用户之间的唯一消息,但我认为我的查询不正确。我也会把我找到的查询放在上面,以防它对你有帮助。

select *
  from message
  where id_destinataire_id=3 
  or id_emmeteur_id=1 
  AND ( id, least(id_emmeteur_id, id_destinataire_id), greatest(id_emmeteur_id, id_destinataire_id))     
  in 
  (  select 
           max(id) 
         , least(id_emmeteur_id, id_destinataire_id) 
         , greatest(id_emmeteur_id, id_destinataire_id)
      from message
      group by id_emmeteur_id, id_destinataire_id
  )

提前感谢您的帮助。 (希望我的数据库结构没有错)

【问题讨论】:

【参考方案1】:

你可以使用not exists如下:

select t.*
  from your_table t
 where 1 in (t.id_emmeteur_id, t.id_destinataire_id)
  and not exists 
      (select * from your_table tt
        where t.id_emmeteur_id = tt.id_emmeteur_id  
          and t.id_destinataire_id = tt.id_destinataire_id)
          and tt.id > t.id)

【讨论】:

感谢您的回答。我已经尝试过您的解决方案,但我只有最新消息(即“另一个用户”)而不是之前的消息。也许我误解了我的问题。使用我提出的问题的示例,我想要消息“另一个用户”(我可以使用您的解决方案)和“测试号 3”(但不幸的是,此消息未被检索)。 这似乎更好,但我有不止一条关于 ID 的“1”和“3”的消息。我会尝试使用您更新的解决方案。也许如果我可以在此查询中的某处放置一个 LIMIT 子句,是否可以每个用户 ID 只有一条消息?【参考方案2】:

一种方法是通过 to 和 from id 获取最后一个对话,并基于此过滤结果。

WITH LastMessages AS 
(
    SELECT 
        id,
        ROW_NUMBER()OVER(PARTITION BY id_destinataire_id ORDER BY ID) last_destinataire_id,
        ROW_NUMBER()OVER(PARTITION BY id_emmeteur_id ORDER BY ID) last_emmeteur_id,
        id_destinataire_id to_id,
        id_emmeteur_id from_id
        contenu
    FROM
        message
    WHERE
        id_emmeteur_id = 3 OR id_destinataire_id = 3
)
SELECT 
    id, to_id, from_id, contenu
FROM 
    LastMessages 
WHERE
    last_destinataire_id = 1 OR  last_emmeteur_id = 1
ORDER BY
    id;

无 CTE 版本

SELECT 
    id,
    to_id,
    from_id,
    contenu
FROM 
(
    SELECT 
        id,
        last_destinataire_id = ROW_NUMBER()OVER(PARTITION BY id_destinataire_id ORDER BY ID),
        last_emmeteur_id = ROW_NUMBER()OVER(PARTITION BY id_emmeteur_id ORDER BY ID),
        to_id = id_destinataire_id,
        from_id =  id_emmeteur_id,
        contenu
    FROM
        message
    WHERE
        id_emmeteur_id = 3 OR id_destinataire_id = 3
)AS X
WHERE
    last_destinataire_id = 1 OR  last_emmeteur_id = 1
ORDER BY
    id

【讨论】:

我忘了准确地说我正在使用 mysql(我想是的)和 phpMyAdmin(它是一个 Web 项目),并且在我的情况下不支持 WITH 子句。是否可以使用 WITH 以外的其他子句?另外,谢谢您的回答。 使用非 CTE 解决方案更新 我实际上正在寻找如何升级我的 MySQL 版本(我使用的是 5.7 版本),因为我认为“ROW_NUMBER()”函数仅适用于 8.0 或更高版本。我正在使用 MAMP 打开本地服务器,但似乎他们无法升级到比 5.7 更好的版本。我实际上专注于如何升级到 8.0 版本(如果可能的话),我会告诉你我是否找到了适合我的解决方案。再次感谢您的帮助。 你好。我回到这里是因为我无法升级我的 MySQL 版本。我还读到 ROW_NUMBER() 与我的想法不同,它与 MySQL 不兼容。我试图用我将在下面发布的代码“模拟”一个行号,但没有成功。 MySQL 5.7 版本还有另一种可能性吗?很抱歉给您带来不便。 实际SQL查询:SET @rownumber = 0; SELECT id, to_id, from_id, contenu FROM ( SELECT id, (@rownumber:=@rownumber+1) AS last_destinataire_id ORDER BY ID, (@rownumber:=@rownumber+1) AS last_emmeteur_id ORDER BY ID, to_id = id_destinataire_id, from_id = id_emmeteur_id, contenu FROM message WHERE id_emmeteur_id = 3 OR id_destinataire_id = 3 )AS X WHERE last_destinataire_id = 1 OR last_emmeteur_id = 1 ORDER BY id

以上是关于在 SQL 中选择发送者和接收者之间的最后一条消息的主要内容,如果未能解决你的问题,请参考以下文章

PHP Socket Client 只发送和接收一条消息

MySQL - 根据字段选择LEFT JOIN列的最后几行。

ZooKeeper-简介

分布式计算泛型

当值与 SQL Server 中的最后一条记录之间存在特定差距时如何选择值

断开连接后从 xmpp 服务器检索最后发送的消息