SQL从两个表中获取对话

时间:2012-11-29 22:37:52

标签: mysql sql

我有两个表,一个存储传入的消息,另一个存储传出的消息。我想要的是能够有一个消息的对话视图,以便来自/到同一用户ID的所有传入和传出消息被分组,并且对话按最新消息(进或出)排序

Outgoing
----------
user_id
time
message

Incoming
----------
user_id
time
message

我想要的是显示结果,例如

-> User A   9:10 pm   Nice ...
<- User A   8:45 pm   Our special is pepperoni!
-> User A   8:00 pm   What's your special dish?

<- User B   9:00 pm   We open at 5
-> User B   6:56 pm   Hello What time to you open?

<- User C   8:43 pm   Thanks!
-> User C   4:00 pm   Loved the pizza today!!

知道如何编写查询来执行此操作吗?

修改

如果用户B然后重新输入文本,结果应为:

-> User B   9:15 pm   Ok great!
<- User B   9:00 pm   We open at 5
-> User B   6:56 pm   Hello What time to you open?

-> User A   9:10 pm   Nice ...
<- User A   8:45 pm   Our special is pepperoni!
-> User A   8:00 pm   What's your special dish?

<- User C   8:43 pm   Thanks!
-> User C   4:00 pm   Loved the pizza today!!

4 个答案:

答案 0 :(得分:2)

您需要UNION这两个表并相应地排序(ORDER BY):

SELECT 
    '<-' AS direction,  user_id,  time,  message
FROM
    Outgoing

UNION ALL

SELECT 
    '->',               user_id,  time,  message
FROM
    Incoming

ORDER BY
    user_id ASC,
    time DESC ;

在复杂排序的附加说明之后:

SELECT
  CASE WHEN m.d = 1 THEN '<-' ELSE '->' END AS direction, 
  m.user_id, m.time, m.message
FROM
    ( SELECT 
        u.user_id, 
        GREATEST( COALESCE(mo.time, mi.time), 
                  COALESCE(mi.time, mo.time) ) AS maxtime
      FROM 
          ( SELECT user_id  FROM Outgoing
          UNION
            SELECT user_id  FROM Incoming 
          ) AS u
        LEFT JOIN
          ( SELECT user_id, MAX(time) AS time  FROM Outgoing  GROUP BY user_id
          ) AS mo
          ON mo.user_id = u.user_id
        LEFT JOIN
          ( SELECT user_id, MAX(time) AS time  FROM Incoming  GROUP BY user_id
          ) AS mi
          ON mi.user_id = u.user_id
    ) AS b    
  JOIN
    ( SELECT 1 AS d, user_id, time, message  FROM Outgoing
    UNION ALL
      SELECT 2 AS d, user_id, time, message  FROM Incoming
    ) AS m
    ON m.user_id = b.user_id
ORDER BY
    b.maxtime ASC,
    m.user_id ASC,
    m.time DESC ;

答案 1 :(得分:1)

这样的事情应该按用户和时间得到结果。您需要在应用程序级别处理显示以显示每个用户的消息:

select * from (
  select '->' as direction, o.* from outgoing o
  union
  select '<-' as direction, i.* from incoming i
) M
order by user_id asc, time desc

示例输出:

| DIRECTION | USER_ID |                            TIME |                      MESSAGE |
----------------------------------------------------------------------------------------
|        -> |       1 | November, 29 2012 21:10:00+0000 |                     Nice ... |
|        <- |       1 | November, 29 2012 20:45:00+0000 |    Our special is pepperoni! |
|        -> |       1 | November, 29 2012 20:00:00+0000 |   What''s your special dish? |
|        <- |       2 | November, 29 2012 21:00:00+0000 |                 We open at 5 |
|        -> |       2 | November, 29 2012 18:56:00+0000 | Hello What time to you open? |
|        <- |       3 | November, 29 2012 20:43:00+0000 |                      Thanks! |
|        -> |       3 | November, 29 2012 16:00:00+0000 |      Loved the pizza today!! |

演示:http://www.sqlfiddle.com/#!2/602c1/11

答案 2 :(得分:1)

就个人而言,我并不喜欢你的桌子结构。对一个用户传入的消息是对另一个用户的传出消息,这意味着您需要复制每个表中系统中的每条消息。

我可能只有一个带有to和from字段的消息表。如果您有一个这样的表:

message_id (primary key)
from_user_id (indexed)
to_user_id (indexed)
message
time (indexed)

您的查询很简单:

SELECT *
FROM messages
WHERE from_user_id = ? OR to_user_id = ?
ORDER BY time DESC

请注意,这并不会为您显示的方式提供简单的查询(您需要进行一些查询后数据操作)。但它确实为您提供了最有效的查询查询,并防止您需要在存储中两次复制邮件。

如果你需要坚持分组对话的概念(甚至扩展到多方消息),那么也许你可以看看有一个对话表并修改你的模式是这样的:

对话(多对多联接表)

conversation_id (indexed)
user_id (indexed)
(compound primary key across both fields)

消息

message_id (primary key)
conversation_id (indexed)
sending_user_id
message
time (indexed)

使用像这样的查询

SELECT m.sending_user_id, m.message, m.time
FROM conversations AS c
INNER JOIN messages AS m ON c.conversation_id = m.conversation_id
WHERE c.user_id = ?
ORDER BY c.conversation_id, m.time DESC

显然,从结果查询中,如果sending_user_id等于当前用户的id,则它是传出消息,否则它是来自其他对话参与者的消息。

答案 3 :(得分:0)

为什么单独表?您可以将它们放在同一个表中,并添加一个类型为bit的列,其中1和0表示传入和传出。然后您的查询就像:

一样简单
select user_id, time, message, inout from message order by user_id, time

对我来说,方向是告诉你一些关于消息的信息,无论哪种方式仍然是消息。

如果你仍然必须以另一种方式去做,那么你将不得不做一个联盟,但期望性能更差。您可以通过前台设计为您提供最佳性能调整。

with message as (
select user_id, time, message, 'incoming' from incoming
union all
select user_id, time, message, 'outgoing' from outgoing
) select * from message order by user_id, time

或类似的......

另外,你应该警惕按时间字段排序。根据经验,如果两条消息同时进入,您会发现意外结果。这很可能是因为您的示例只是细粒度到分钟,而不是秒或微秒。更好的方法是使用按升序自动分配的数字PK。这样,如果时间不是唯一的,你仍然有办法确定秩序。