SQL:如何按两列的唯一组合进行分组?
SQL: How do I group by a unique combination of two columns?
上下文:
- A table
message
有列 from_user_id
和 to_user_id
- 用户应该看到最近的对话以及显示的最后一条消息
- 一个会话由多条消息组成,这些消息具有相同的用户 ID 组合(用户发送消息,用户接收消息)
Table内容:
+-------------------------------------------------+--------------+------------+
| text | from_user_id | to_user_id |
+-------------------------------------------------+--------------+------------+
| Hi there! | 13 | 14 | <- Liara to Penelope
| Oh hi, how are you? | 14 | 13 | <- Penelope to Liara
| Fine, thanks for asking. How are you? | 13 | 14 | <- Liara to Penelope
| Could not be better! How are things over there? | 14 | 13 | <- Penelope to Liara
| Hi, I just spoke to Penelope! | 13 | 15 | <- Liara to Zara
| Oh you did? How is she? | 15 | 13 | <- Zara to Liara
| Liara told me you guys texted, how are things? | 15 | 14 | <- Zara to Penelope
| Fine, she's good, too | 14 | 15 | <- Penelope to Zara
+-------------------------------------------------+--------------+------------+
我尝试按from_user_id
和to_user_id
进行分组,但我显然得到了一组用户收到的消息和另一组用户发送的消息。
SELECT text, from_user_id, to_user_id,created FROM message
WHERE from_user_id=13 or to_user_id=13
GROUP BY from_user_id, to_user_id
ORDER BY created DESC
得到我:
+-------------------------------+--------------+------------+---------------------+
| text | from_user_id | to_user_id | created |
+-------------------------------+--------------+------------+---------------------+
| Oh you did? How is she? | 15 | 13 | 2017-09-01 21:45:14 | <- received by Liara
| Hi, I just spoke to Penelope! | 13 | 15 | 2017-09-01 21:44:51 | <- send by Liara
| Oh hi, how are you? | 14 | 13 | 2017-09-01 17:06:53 |
| Hi there! | 13 | 14 | 2017-09-01 17:06:29 |
+-------------------------------+--------------+------------+---------------------+
虽然我想要:
+-------------------------------+--------------+------------+---------------------+
| text | from_user_id | to_user_id | created |
+-------------------------------+--------------+------------+---------------------+
| Oh you did? How is she? | 15 | 13 | 2017-09-01 21:45:14 | <- Last message of conversation with Zara
| Oh hi, how are you? | 14 | 13 | 2017-09-01 17:06:53 |
+-------------------------------+--------------+------------+---------------------+
我怎样才能做到这一点?
编辑:
使用 least
或 greatest
也不会导致所需的结果。
它确实对条目进行了正确分组,但正如您在结果中看到的那样,最后一条消息不正确。
+----+-------------------------------------------------+------+---------------------+--------------+------------+
| id | text | read | created | from_user_id | to_user_id |
+----+-------------------------------------------------+------+---------------------+--------------+------------+
| 8 | Oh you did? How is she? | No | 2017-09-01 21:45:14 | 15 | 13 |
| 5 | Could not be better! How are things over there? | No | 2017-09-01 17:07:47 | 14 | 13 |
+----+-------------------------------------------------+------+---------------------+--------------+------------+
做你想做的事情的一种方法是使用相关子查询,找到为匹配对话创建的最小值 date/time:
SELECT m.*
FROM message m
WHERE 13 in (from_user_id, to_user_id) AND
m.created = (SELECT MAX(m2.created)
FROM message m2
WHERE (m2.from_user_id = m.from_user_id AND m2.to_user_id = m.to_user_id) OR
(m2.from_user_id = m.to_user_id AND m2.to_user_id = m.from_user_id)
)
ORDER BY m.created DESC
我使用 GREATEST
和 LEAST
为每个对话创建一个 grp。然后对该 grp 进行排序并根据时间分配行号。
SELECT *
FROM (
SELECT LEAST(`from_user_id`, `to_user_id`) as L,
GREATEST(`from_user_id`, `to_user_id`) as G,
`text`,
CONCAT (LEAST(`from_user_id`, `to_user_id`), '-', GREATEST(`from_user_id`, `to_user_id`)) as grp,
@rn := if(@grp = CONCAT(LEAST(`from_user_id`, `to_user_id`), '-', GREATEST(`from_user_id`, `to_user_id`)),
@rn + 1,
if(@grp := CONCAT(LEAST(`from_user_id`, `to_user_id`), '-', GREATEST(`from_user_id`, `to_user_id`)), 1, 1)
) as rn,
`time`
FROM Table1
CROSS JOIN (SELECT @rn := 0, @grp := '') as var
ORDER BY LEAST(`from_user_id`, `to_user_id`),
GREATEST(`from_user_id`, `to_user_id`),
`time` DESC
) T
WHERE rn = 1;
输出
编辑:最后你需要从对话中过滤掉 13。
WHERE rn = 1
AND 13 IN (`L`, `G`);
与#13 的最后一次对话?在更新的 DBMS 中,您将使用 row_number()
来查找这些。在 MySQL 中你可以使用 not exists
,以确保对话伙伴没有更晚的 post。顺便说一句,您可以使用 from_user_id + to_user_id - 13
轻松找到合作伙伴的号码。 (而当比较两条记录时,你可以直接使用from_user_id + to_user_id
。)
select text, from_user_id, to_user_id, created
from message m1
where 13 in (from_user_id, to_user_id)
and not exists
(
select *
from message m2
where 13 in (m2.from_user_id, m2.to_user_id)
and m2.from_user_id + m2.to_user_id = m1.from_user_id + m1.to_user_id
and m2.created > m1.created
);
上下文:
- A table
message
有列from_user_id
和to_user_id
- 用户应该看到最近的对话以及显示的最后一条消息
- 一个会话由多条消息组成,这些消息具有相同的用户 ID 组合(用户发送消息,用户接收消息)
Table内容:
+-------------------------------------------------+--------------+------------+
| text | from_user_id | to_user_id |
+-------------------------------------------------+--------------+------------+
| Hi there! | 13 | 14 | <- Liara to Penelope
| Oh hi, how are you? | 14 | 13 | <- Penelope to Liara
| Fine, thanks for asking. How are you? | 13 | 14 | <- Liara to Penelope
| Could not be better! How are things over there? | 14 | 13 | <- Penelope to Liara
| Hi, I just spoke to Penelope! | 13 | 15 | <- Liara to Zara
| Oh you did? How is she? | 15 | 13 | <- Zara to Liara
| Liara told me you guys texted, how are things? | 15 | 14 | <- Zara to Penelope
| Fine, she's good, too | 14 | 15 | <- Penelope to Zara
+-------------------------------------------------+--------------+------------+
我尝试按from_user_id
和to_user_id
进行分组,但我显然得到了一组用户收到的消息和另一组用户发送的消息。
SELECT text, from_user_id, to_user_id,created FROM message
WHERE from_user_id=13 or to_user_id=13
GROUP BY from_user_id, to_user_id
ORDER BY created DESC
得到我:
+-------------------------------+--------------+------------+---------------------+
| text | from_user_id | to_user_id | created |
+-------------------------------+--------------+------------+---------------------+
| Oh you did? How is she? | 15 | 13 | 2017-09-01 21:45:14 | <- received by Liara
| Hi, I just spoke to Penelope! | 13 | 15 | 2017-09-01 21:44:51 | <- send by Liara
| Oh hi, how are you? | 14 | 13 | 2017-09-01 17:06:53 |
| Hi there! | 13 | 14 | 2017-09-01 17:06:29 |
+-------------------------------+--------------+------------+---------------------+
虽然我想要:
+-------------------------------+--------------+------------+---------------------+
| text | from_user_id | to_user_id | created |
+-------------------------------+--------------+------------+---------------------+
| Oh you did? How is she? | 15 | 13 | 2017-09-01 21:45:14 | <- Last message of conversation with Zara
| Oh hi, how are you? | 14 | 13 | 2017-09-01 17:06:53 |
+-------------------------------+--------------+------------+---------------------+
我怎样才能做到这一点?
编辑:
使用 least
或 greatest
也不会导致所需的结果。
它确实对条目进行了正确分组,但正如您在结果中看到的那样,最后一条消息不正确。
+----+-------------------------------------------------+------+---------------------+--------------+------------+
| id | text | read | created | from_user_id | to_user_id |
+----+-------------------------------------------------+------+---------------------+--------------+------------+
| 8 | Oh you did? How is she? | No | 2017-09-01 21:45:14 | 15 | 13 |
| 5 | Could not be better! How are things over there? | No | 2017-09-01 17:07:47 | 14 | 13 |
+----+-------------------------------------------------+------+---------------------+--------------+------------+
做你想做的事情的一种方法是使用相关子查询,找到为匹配对话创建的最小值 date/time:
SELECT m.*
FROM message m
WHERE 13 in (from_user_id, to_user_id) AND
m.created = (SELECT MAX(m2.created)
FROM message m2
WHERE (m2.from_user_id = m.from_user_id AND m2.to_user_id = m.to_user_id) OR
(m2.from_user_id = m.to_user_id AND m2.to_user_id = m.from_user_id)
)
ORDER BY m.created DESC
我使用 GREATEST
和 LEAST
为每个对话创建一个 grp。然后对该 grp 进行排序并根据时间分配行号。
SELECT *
FROM (
SELECT LEAST(`from_user_id`, `to_user_id`) as L,
GREATEST(`from_user_id`, `to_user_id`) as G,
`text`,
CONCAT (LEAST(`from_user_id`, `to_user_id`), '-', GREATEST(`from_user_id`, `to_user_id`)) as grp,
@rn := if(@grp = CONCAT(LEAST(`from_user_id`, `to_user_id`), '-', GREATEST(`from_user_id`, `to_user_id`)),
@rn + 1,
if(@grp := CONCAT(LEAST(`from_user_id`, `to_user_id`), '-', GREATEST(`from_user_id`, `to_user_id`)), 1, 1)
) as rn,
`time`
FROM Table1
CROSS JOIN (SELECT @rn := 0, @grp := '') as var
ORDER BY LEAST(`from_user_id`, `to_user_id`),
GREATEST(`from_user_id`, `to_user_id`),
`time` DESC
) T
WHERE rn = 1;
输出
编辑:最后你需要从对话中过滤掉 13。
WHERE rn = 1
AND 13 IN (`L`, `G`);
与#13 的最后一次对话?在更新的 DBMS 中,您将使用 row_number()
来查找这些。在 MySQL 中你可以使用 not exists
,以确保对话伙伴没有更晚的 post。顺便说一句,您可以使用 from_user_id + to_user_id - 13
轻松找到合作伙伴的号码。 (而当比较两条记录时,你可以直接使用from_user_id + to_user_id
。)
select text, from_user_id, to_user_id, created
from message m1
where 13 in (from_user_id, to_user_id)
and not exists
(
select *
from message m2
where 13 in (m2.from_user_id, m2.to_user_id)
and m2.from_user_id + m2.to_user_id = m1.from_user_id + m1.to_user_id
and m2.created > m1.created
);