【问题标题】:SQL: How do I GROUP BY two columns as unordered pairs?SQL:如何将两列分组为无序对?
【发布时间】:2015-08-21 08:15:49
【问题描述】:

我正在用 MySQL 实现一个聊天系统,使用 SQLAlchemy 作为我的 ORM。
我的表有以下列:id, from_user_id, to_user_id, message, time_sent.

我正在尝试创建一个查询,在该查询中我选择包含从每个对话发送的最新消息的记录。这与人们在 Facebook 上看到的对话列表完全相同。
关于如何做到这一点的任何想法?
这是我目前所拥有的:

latest_msg_sq = (DBSession.query(func.max(Message.id).label("max_id"))
.group_by(Message.from_user)).subquery()    
matching_conv = DBSession.query(Message)
.join(latest_msg_sq, and_(Message.id ==  
latest_msg_sq.c.max_id)).order_by(Message.time_sent.desc()) 

不幸的是,它不能很好地工作,因为它只获取 from_user_id 的最新消息。我还需要它来考虑来自 to_user_id 的消息,而 group_by 似乎不是最好的方法。在我看来,我认为我需要将记录 from_user_id 和 to_user_id 视为无序对,然后在这些对上使用 group_by。但是,我不知道该怎么做。

【问题讨论】:

  • 聊天应用程序通常有一个 room 抽象,它们使用自己的唯一 id 实现。与其尝试将from_user_id 加上to_user_id 变成对话,不如让messages 表具有以下列:id, owner, room, message, time_sent,这样您可以通过room 选择并按@ 排序987654328@.

标签: python mysql sql sqlalchemy


【解决方案1】:

在 MySQL 中,您将使用 least()greatest()。获取最近的时间:

select least(from_user_id, to_user_id) as u1, greatest(from_user_id, to_user_id) as u2,
       max(time_sent) as maxts
from messages
group by least(from_user_id, to_user_id), greatest(from_user_id, to_user_id)

要获取完整的消息,您可以重新加入:

select m.*
from (select least(from_user_id, to_user_id) as u1, greatest(from_user_id, to_user_id) as u2,
             max(time_sent) as maxts
      from messages
      group by least(from_user_id, to_user_id), greatest(from_user_id, to_user_id)
     ) m2 join
     messages m
     on least(m2.from_user_id, m2.to_user_id) = least(m.from_user_id, m.to_user_id) and
        greatest(m2.from_user_id, m2.to_user_id) = greatest(m.from_user_id, m.to_user_id)

我不确定您将如何在 SQLalchemy 中表达这一点。

【讨论】:

    【解决方案2】:

    试试这个:

    select msg1.* from message msg1
    left outer join message msg2 on 
    (msg1.from_user_id = msg2.from_user_id 
    and msg1.to_user_id = msg2.to_user_id and msg2.time_sent > msg1.time_sent)
    left outer join message msg3 on 
    msg1.to_user_id = msg3.from_user_id 
    and msg1.from_user_id= msg3.to_user_id and msg3.time_sent > msg1.time_sent
    where msg2.id is null and msg3.id is null
    

    【讨论】:

    • 关于如何在 SQLAlchemy 中执行此操作的任何想法?
    • 它们是简单的左连接。我不熟悉 SQLAlchemy,但任何 ORM 都支持左连接
    【解决方案3】:

    SQLAlchemy 版本的 Mohsen Heydari 为任何需要它的人提供答案:

    msg1 = aliased(Message)
    msg2 = aliased(Message)
    msg3 = aliased(Message)
    matching_conv = DBSession.query(msg1)
    matching_conv = matching_conv.outerjoin(msg2, and_(msg1.from_user==msg2.from_user, msg1.to_user==msg2.to_user, msg2.id > msg1.id))
    matching_conv = matching_conv.outerjoin(msg3, and_(msg1.to_user==msg3.from_user, msg1.from_user==msg3.to_user, msg3.id > msg1.id))
    matching_conv = matching_conv.filter(and_(msg2.id == None, msg3.id == None)).order_by(msg1.time_sent.desc())
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-01-28
      • 1970-01-01
      • 2014-01-31
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多