【问题标题】:Private messaging system. Listing last message of each conversation私人消息系统。列出每个对话的最后一条消息
【发布时间】:2014-11-04 11:07:54
【问题描述】:

假设这是数据库结构:

SELECT * FROM `pms` where id_to = 1 or id_from = 1

这将返回他已接收或发送的所有消息,

那么如何从用户 1 可能进行的每个对话中检索最后一条消息?

PD:当两个用户之间有一条或多条消息时,我称之为对话

-编辑-

所以给定这个数据库内容:

我们想得到 id 4 和 6

【问题讨论】:

    标签: mysql sql


    【解决方案1】:

    这假设id 是一个自增列:

    SELECT MAX(id) AS id
    FROM pms
    WHERE id_to = 1 OR id_from = 1
    GROUP BY (IF(id_to = 1, id_from, id_to))
    

    假设您有 id_fromid_to 索引,这种变体很可能会表现得更好,因为 MySQL 不知道如何处理 OR:

    SELECT MAX(id) AS id FROM
    (SELECT id, id_from AS id_with
    FROM pms
    WHERE id_to = 1
    UNION ALL
    SELECT id, id_to AS id_with
    FROM pms
    WHERE id_from = 1) t
    GROUP BY id_with
    

    以下是获取这些 id 的消息的方法:

    SELECT * FROM pms WHERE id IN
        (SELECT MAX(id) AS id FROM
        (SELECT id, id_from AS id_with
        FROM pms
        WHERE id_to = 1
        UNION ALL
        SELECT id, id_to AS id_with
        FROM pms
        WHERE id_from = 1) t
        GROUP BY id_with)
    

    【讨论】:

    • 这似乎有效!我会给它一些测试,让你知道。谢谢!
    • @ToniMichelCaubet,另一个更新返回整个消息。
    • 我不会说..你可以想象我不是任何 sql 大师。另外,我的数据库记录很少。顺便说一句,我正在使用它;)
    【解决方案2】:
    select pms.* from pms 
    inner join 
        (select max(fecha) as max_fecha,
               if(id_to<id_from,id_to,id_from) min_id, 
               if(id_to<id_from,id_from,id_to) max_id
          from pms where id_to = 1 or id_from = 1 
             group by if(id_to<id_from,id_to,id_from),if(id_to<id_from,id_from,id_to)) t 
         on (if(pms.id_to<pms.id_from,pms.id_to,pms.id_from)=t.min_id) 
            and (if(pms.id_to<pms.id_from,pms.id_from,pms.id_to)=t.max_id) 
            and (pms.fecha=t.max_fecha)
    

    此外,如果您的表中的 id_to 和 id_from 足够小以防止语句 (id_to+id_from) 溢出,这里是简单的查询:

    select pms.* from pms 
    inner join 
        (select max(fecha) as max_fecha, id_to+id_from as sum_id
          from pms where id_to = 1 or id_from = 1 
             group by id_to+id_from) t 
         on ((pms.id_to+pms.id_from)=t.sum_id) 
            and (pms.fecha=t.max_fecha)
     where pms.id_to = 1 or pms.id_from = 1
    

    【讨论】:

    • 这是显示每个对话的每个用户的最后一条消息。它关闭了,但我们只想要每个对话的最后一条消息
    • @ToniMichelCaubet,这真的是你要使用的吗?
    • 你的表现更好?唯一的原因是因为我必须将所有其他行添加到您的查询中并将 max(id) 重命名为 id ... @MarcusAdams
    • 工作正常!但是将“GROUP BY id_to+id_from”替换为:“GROUP BY sum_id”。这是多余的:“((pms.id_to+pms.id_from)=t.sum_id)”,“WHERE pms.id_to = 1 or pms.id_from = 1”也是多余的;删除它,代码会更短/更高效。
    【解决方案3】:

    这个查询应该可以工作:

    SELECT a.*
    FROM pms a
         INNER JOIN (
                     SELECT id_to, id_from, MAX(fecha) AS fecha
                     FROM pms
                     WHERE (id_to = 1 OR id_from = 1)
                     GROUP BY LEAST(id_to, id_from)
                    ) b
                    ON a.fecha = b.fecha AND
                       (a.id_to = b.id_to OR
                       a.id_from = b.id_from);
    

    See example @ sqlfiddle here

    如果您将id 设置为PRIMARY KEY 并且按时间顺序记录消息,则可以进一步优化和简化为:

    SELECT a.*
    FROM pms a
         INNER JOIN (
                     SELECT MAX(id) AS id
                     FROM pms
                     WHERE (id_to = 1 OR id_from = 1)
                     GROUP BY LEAST(id_to, id_from)
                    ) b
                    ON a.id = b.id;
    

    【讨论】:

    • 报错如下:每个派生表必须有自己的别名
    • 哦,好吧.. 忘记了派生表 b 的别名。更新查询,立即尝试。
    • gosh... 报以下错误:where 子句中的Column 'id_to' is ambiguous
    • 现在可以工作,但会给出每个对话的每个用户的最后一条消息。它关闭了,但我们只想要每个对话的最后一条消息
    • 问题是每次对话都会发送 2 条消息,我只想要最后一条..
    猜你喜欢
    • 2018-10-09
    • 1970-01-01
    • 2012-09-09
    • 2016-03-24
    • 1970-01-01
    • 2016-05-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多