我正在尝试实现一个与facebook非常相似的消息系统。消息表是:
+--------+----------+--------+-----+----------+
| msg_id | msg_from | msg_to | msg | msg_time |
+--------+----------+--------+-----+----------+
此处msg_from
和msg_to
包含用户ID,msg_time
包含邮件的时间戳。用户的用户ID既可以出现在to和from列中,也可以出现在另一个用户的多次。我应该如何编写一个SQL查询来选择两个用户之间最近发送的消息? (消息可以来自任何一个)1到2或2到1。
答案 0 :(得分:3)
由于John Woo澄清它不是方向性的,这是我的新答案:
select *
from msgsList
where (least(msg_from, msg_to), greatest(msg_from, msg_to), msg_time)
in
(
select
least(msg_from, msg_to) as x, greatest(msg_from, msg_to) as y,
max(msg_time) as msg_time
from msgsList
group by x, y
);
输出:
| MSG_ID | MSG_FROM | MSG_TO | MSG | MSG_TIME |
------------------------------------------------------------------------
| 1 | 1 | 2 | hello | January, 23 2010 17:00:00-0800 |
| 5 | 1 | 3 | me too | January, 23 2012 00:15:00-0800 |
| 6 | 3 | 2 | hello | January, 23 2012 01:12:12-0800 |
对于此输入:
create table msgsList
(
msg_id int,
msg_from int,
msg_to int,
msg varchar(10),
msg_time datetime
);
insert into msgslist VALUES
(1, 1, 2, 'hello', '2010-01-23 17:00:00'), -- shown
(2, 2, 1, 'world', '2010-01-23 16:00:00'),
(3, 3, 1, 'i am alive', '2011-01-23 00:00:00'),
(4, 3, 1, 'really', '2011-01-22 23:15:00'),
(5, 1, 3, 'me too', '2012-01-23 00:15:00'), -- shown
(6, 3, 2, 'hello', '2012-01-23 01:12:12'); -- shown
如果ANSI SQL是你的一杯茶,请按照以下方式进行:http://sqlfiddle.com/#!2/0a575/19
select *
from msgsList z
where exists
(
select null
from msgsList
where
least(z.msg_from, z.msg_to) = least(msg_from, msg_to)
and greatest(z.msg_from, z.msg_to) = greatest(msg_from, msg_to)
group by least(msg_from, msg_to), greatest(msg_from, msg_to)
having max(msg_time) = z.msg_time
) ;
答案 1 :(得分:2)
难道这么简单吗? http://www.sqlfiddle.com/#!2/50f9f/1
set @User1 := 'John';
set @User2 := 'Paul';
select *
from
(
select *
from messages
where msg_from = @User1 and msg_to = @User2
order by msg_time desc
limit 1
) as x
union
select *
from
(
select *
from messages
where msg_from = @User2 and msg_to = @User1
order by msg_time desc
limit 1
) as x
order by msg_time desc
输出:
| MSG_ID | MSG_FROM | MSG_TO | MSG | MSG_TIME |
----------------------------------------------------------------------------
| 2 | Paul | John | Hey Johnny! | August, 20 2012 00:00:00-0700 |
| 1 | John | Paul | Hey Paulie! | August, 19 2012 00:00:00-0700 |
如果只支持MySQL支持窗口函数,可能会简单得多:http://www.sqlfiddle.com/#!1/e4781/8
with recent_message as
(
select *, rank() over(partition by msg_from, msg_to order by msg_time desc) as r
from messages
)
select *
from recent_message
where r = 1
and
(
(msg_from = 'John' and msg_to = 'Paul')
or
(msg_from = 'Paul' and msg_to = 'John')
)
order by msg_time desc;
答案 2 :(得分:1)
对于像这样的任何复杂查询,请使用TDQD - 测试驱动的查询设计。逐步设计答案,根据您的经验控制步骤的大小以及您对问题的理解程度。
在整个过程中,我假设用户ID是整数;我正在使用值1000和2000。
SELECT MAX(msg_time) AS msg_time
FROM message
WHERE ((msg_to = 1000 AND msg_from = 2000) OR
(msg_to = 2000 AND msg_from = 1000)
)
SELECT m.*
FROM message AS m
JOIN (SELECT MAX(msg_time) AS msg_time
FROM message
WHERE ((msg_to = 1000 AND msg_from = 2000) OR
(msg_to = 2000 AND msg_from = 1000)
)
) AS t
ON t.msg_time = m.msg_time
WHERE ((m.msg_to = 1000 AND m.msg_from = 2000) OR
(m.msg_to = 2000 AND m.msg_from = 1000)
)
如果在具有相同最新时间戳的这些字符之间恰好有两条(或更多条)消息,则它们都将被选中;目前在碰撞之间没有选择的依据。如果您认为这是一个问题,您可以安排使用上面的查询找到MAX(msg_id)
(作为子查询):
SELECT m2.*
FROM message AS m2
JOIN (SELECT MAX(m.msg_id) AS msg_id
FROM message AS m
JOIN (SELECT MAX(msg_time) AS msg_time
FROM message
WHERE ((msg_to = 1000 AND msg_from = 2000) OR
(msg_to = 2000 AND msg_from = 1000)
)
) AS t
ON t.msg_time = m.msg_time
WHERE ((m.msg_to = 1000 AND m.msg_from = 2000) OR
(m.msg_to = 2000 AND m.msg_from = 1000)
)
) AS i
ON i.msg_id = m2.msg_id
警告:代码未经任何DBMS正式测试。
答案 3 :(得分:0)
在考虑之后,我想出了这个:
SELECT min_user AS min(msg_from, msg_to), max_user AS max(msg_from, msg_to),
max(msg_date) FROM msg GROUP BY min_user, max_user
我仍然不太确定如何从邮件中获取其他数据,但我会考虑一下。