【问题标题】:Selecting unique records from multiple tables in MySQL从 MySQL 中的多个表中选择唯一记录
【发布时间】:2013-06-19 18:50:19
【问题描述】:

设置

我有一个包含两个表的数据库。其中一个用于收件箱消息 (inbox),另一个用于发件箱消息 (outbox)。它们都有相同的结构和一些记录。

+------------------------------------------------------------+
| inbox                                                      |
+------------------------------------------------------------+
| messageID | from  | to    | messageText | timestamp        |
+------------------------------------------------------------+
| 1         | userA | userB | sometext    | 2013-06-19 10:30 |
| 2         | userB | userC | sometext    | 2013-06-19 10:40 |
| 3         | userC | userA | sometext    | 2013-06-19 10:50 |
+------------------------------------------------------------+

+------------------------------------------------------------+
| outbox                                                     |
+------------------------------------------------------------+
| messageID | from  | to    | messageText | timestamp        |
+------------------------------------------------------------+
| 1         | userA | userC | sometext    | 2013-06-19 10:20 |
| 2         | userC | userB | sometext    | 2013-06-19 10:30 |
| 3         | userB | userA | sometext    | 2013-06-19 10:35 |
+------------------------------------------------------------+

问题

我需要从特定用户的每个对话中选择每条最后一条消息(在这种情况下 - 我想从 userA 的对话中检索每条最后一条消息)在消息所在的任何表中 - 收件箱或发件箱。我已经设法通过以下查询从收件箱和发件箱中为每个对话选择最后一条消息:

SELECT * FROM
(
    SELECT from, to, timestamp, messageText, messageID
    FROM inbox
    WHERE to = 'userA'
    ORDER BY timestamp DESC
)
AS tmp_table GROUP BY from
UNION
SELECT * FROM
(
    SELECT from, to, timestamp, messageText, messageID
    FROM outbox 
    WHERE from = 'userA'
    ORDER BY timestamp DESC
)
AS tmp_table GROUP BY to
ORDER BY timestamp DESC

所以现在我需要检查哪条消息较新 - 收件箱中的消息或发件箱中的消息(当然,如果两个用户之间的两个表中都存在消息)并且只返回最新消息。

或者我的方法可能是彻头彻尾的愚蠢 - 请评论:D 谢谢。

【问题讨论】:

  • 老实说,没有理由将这些数据存储在两个单独的表中。
  • 我同意@Jessica,一张桌子和担心。尝试添加`limit 1'
  • @Jessica - 是的,我现在 - 我肯定会使用不同的结构,但遗憾的是我坚持使用这个结构,因为“客户”不允许我修改它。
  • @amigura “限制 1”对我有什么帮助?它将从表中返回一行。那不是我的目标。
  • 您说您的目标是检索 userA 的最新消息 - 只是表格中的一行。您想要 每个 用户的最新行吗?

标签: php mysql group-by sql-order-by union


【解决方案1】:

只需从每个表中获取前 1 条记录,然后将结果合并:

SELECT 'outbox', *
FROM outbox
WHERE `from` = 'userA'
ORDER BY timestamp DESC
LIMIT 1

UNION ALL

SELECT 'inbox', *
FROM inbox
WHERE `from` = 'userA'
ORDER BY timestamp DESC
LIMIT 1

注意事项:

1) 注意from 周围的反引号。 FROM 是太阳下几乎每个 sql 数据库中的保留字,因此如果没有语法错误,您的查询将永远无法工作
2) 注意两个子查询中硬编码的'inbox''outbox' 字符串——它们可以告诉你找到的记录来自哪个表。

【讨论】:

  • 你确定这会起作用吗?现在看来,查询将从每个表中选择一行。我可能需要数千行(userA 可以与数千个用户进行对话)。
  • 它将抓取 userA 是from 人的所有消息,但只返回最近的一条,因为它对日期进行降序排序,并且只返回第一行(限制 1)。所以这个查询最多只会返回两行。一封来自收件箱,一封来自发件箱。
  • 很抱歉,我认为我的问题并不清楚。我想检索用户 A 拥有的所有对话的最新消息 - i。 e.如果收件箱中有一条记录,其中 to=userA 和 from=userB,时间戳为 2013-06-01,并且发件箱中有一条记录,其中 from=userA 和 to=userB,时间戳为 2013-06-19,则查询应该返回后一个记录。如果有更多对话(userA 和 userC) - 那么查询也应该返回此对话中的最后一条消息。
【解决方案2】:

根据您的表上设置的索引,可能可以编写一个执行速度更快的查询,但这应该可以:

select 
    sq.main_user, 
    sq.other_user, 
    coalesce(i.messageText, o.messageText) messageText
from 
   (select 
        main_user, other_user, max(msg_time) last_msg 
    from
        (select 
             `from` main_user, `to` other_user, `timestamp` msg_time 
         from outbox
         where `from` = 'userA'
         union all
         select 
             `to` main_user, `from` other_user, `timestamp` msg_time 
         from inbox
         where `to` = 'userA') uq
      group by main_user, other_user) sq
left join 
    outbox o on sq.main_user = o.`from` and sq.last_msg = o.`timestamp`
left join 
    inbox i on sq.main_user = i.`to` and sq.last_msg = i.`timestamp`

(如果您想查看所有用户的对话,而不仅仅是 userA,请从最里面的子查询中省略 where 子句。)

【讨论】:

  • 谢谢你,就像一个魅力。可悲的是,我自己从来没有写过这样的东西。如果您还可以解释这里发生了什么,因为我不知道它是如何工作的:D
  • @August:最里面的查询(别名为uq)选择来自或发往'userA'的所有消息timestamps,'userA'为main_user,另一个用户为(令人惊讶的是)other_user。中间子查询(别名为sq)汇总来自uq 的结果,以获得main_userother_user 的每个组合的最大(即最新)timestamp。然后,主查询可以选择从main_user 上的sq 和最近的timestamp 连接到收件箱和发件箱表 - 对于main_userother_user 的每个组合,两个表中只有一个应该有匹配记录。
  • @August:coalesce 确保从为最新消息填充的任何表的记录中选择 messageText
猜你喜欢
  • 1970-01-01
  • 2023-03-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-12-28
  • 1970-01-01
  • 2011-09-17
  • 1970-01-01
相关资源
最近更新 更多