【问题标题】:SQL: How do I group by a unique combination of two columns?SQL:如何按两列的唯一组合进行分组?
【发布时间】:2017-09-01 20:29:05
【问题描述】:

上下文:

  • 表message 具有from_user_id 和to_user_id 列
  • 用户应该会看到最近的对话并显示最后一条消息
  • 一个会话由多条消息组成,这些消息具有相同的用户 ID 组合(用户发送消息,用户接收消息)

表格内容:

+-------------------------------------------------+--------------+------------+
| 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 |
+----+-------------------------------------------------+------+---------------------+--------------+------------+

【问题讨论】:

  • 我们能不能对 to_user_id 应用最小,对 from_user_id 应用最大,然后对它们进行分组
  • 已经尝试过了,我编辑了对该问题的尝试。分组工作正常,但我没有收到最后一条消息
  • 为什么最终结果中没有14, 15消息?
  • 那些是故意错误的列,我只想要一个用户的消息:WHERE from_user_id=13 or to_user_id=13
  • 那么对于用户 13,您希望所有对话伙伴(14 和 15)都拥有最新的帖子吗?不管#13 是发送者还是接收者?我明白为什么是“哦,你做到了?她怎么样?”对于#15。但为什么不是“再好不过了!那边的情况怎么样?” #14?

标签: mysql sql heidisql


【解决方案1】:

与#13 的最后一次对话?在更新的 DBMS 中,您可以使用 row_number() 来查找这些。在 MySQL 中,您可以使用not exists,以确保对话伙伴没有稍后的帖子。顺便说一句,您可以通过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
);

【讨论】:

  • 你不能比较 id 总和,13 + 5 = 8 + 10 但对话不同
  • @Juan Carlos Oropeza:您缺少WHERE 子句。您的第二对不包含 13,因此不会被选中。
【解决方案2】:

一种做你想做的事的方法是使用相关子查询来找到匹配对话的最小创建日期/时间:

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

【讨论】:

  • 不应该是 MAX(),因为 OP 想要最新消息吗?
  • 是的,应该使用MAX(),但公平地说,我在问题中提供了错误的预期结果。
【解决方案3】:

我使用GREATEST 和LEAST 为每个对话创建一个grp。然后为该 grp 排序并根据时间分配一个行号。

SQL DEMO

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`);

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-04-28
    相关资源
    最近更新 更多