【问题标题】:MySQL LEFT JOIN using MAX & GROUP BY on joined table?MySQL LEFT JOIN 在连接表上使用 MAX 和 GROUP BY?
【发布时间】:2012-12-04 06:55:49
【问题描述】:

我有两个表(成员和活动),我正在尝试用每个成员的最新活动来查询成员。我已经使用两个查询(一个获取成员,第二个使用活动的 max(id) 和 group by(member))和一些代码来合并数据。我确信它可以通过一个查询来完成,但我不能完全解决它。有什么想法吗?

成员表

id, name
 1, Shawn
 2, bob
 3, tom

活动表

id, member_id, code, timestamp, description
 1,         1,  123,     15000, baked a cake
 2,         1,  456,     20000, ate dinner
 3,         2,  789,     21000, drove home
 4,         1,  012,     22000, ate dessert

想要的结果:

id, name,  activity_code, activity_timestamp, activity_description
 1, shawn, 012,           22000,              ate dessert
 2, bob,   789,           21000,              drove home
 3, tom,   null,          null,               null

【问题讨论】:

    标签: mysql


    【解决方案1】:

    “每组最新”问题在 SQL 中非常常见。仅在这个网站上就有无数解决这个问题的例子。

    如果您的时间戳对于每个成员活动都是唯一的:

    SELECT
      m.id,
      m.name,
      a.code activity_code,
      a.timestamp activity_timestamp,
      a.description activity_description
    FROM
      members m
      INNER JOIN activities a ON a.member_id = m.id
    WHERE
      a.timestamp = (SELECT MAX(timestamp) FROM activities WHERE member_id = m.id)
    

    或者,如果您的活动 ID 随时间单调增加:

      ...
    WHERE
      a.id = (SELECT MAX(id) FROM activities WHERE member_id = m.id)
    

    您不需要分组。但查询将受益于activities 上的索引而不是(member_id, timestamp)(member_id, id),分别。


    编辑

    要显示尚未记录活动的任何成员,请使用这样的左连接。

    SELECT
      m.id,
      m.name,
      a.code activity_code,
      a.timestamp activity_timestamp,
      a.description activity_description
    FROM
      members m
      LEFT JOIN activities a ON 
        a.member_id = m.id
        AND a.timestamp = (SELECT MAX(timestamp) FROM activities WHERE member_id = m.id)
    

    请注意,没有WHERE 子句。从语义上讲,WHERE 在连接完成后应用。因此,WHERE 子句将删除 LEFT JOIN 添加的行,从而有效地提供与原始 INNER JOIN 相同的结果。

    但是如果您在连接条件中应用附加谓词权限,则 LEFT JOIN 将按预期工作。

    【讨论】:

    • 这似乎正在删除我从未记录过任何活动的成员 (tom, id=3)。我尝试将 INNER JOIN 更改为 LEFT JOIN。我正在尝试第二个 WHERE 语句,我的时间戳不能保证是唯一的,但 id 是一个唯一的自动递增字段。
    • @ShawnMcBride 查看修改后的答案。
    • 我个人不喜欢条件中的嵌套查询。如果您加入只有 MAX 时间戳的查询,这将是更优化的解决方案,如下面的答案
    • @dzona 当你有一个合适的索引集时,这并不重要。
    【解决方案2】:
    SELECT 
        members.id ,
        members.name,
        activities.code AS activity_code,
        activities.timestamp AS activity_timestamp,
        activities.description AS activity_description
    FROM 
        members
        LEFT JOIN activities
            ON members.id = activities.member_id
        LEFT JOIN 
            (
                SELECT
                    activities.member_id
                    MAX(activities.id) AS id
                FROM activities
                GROUP BY 
                    activities.member_id
            ) AS t1
            ON activities.id = t1.id
    WHERE
        t1.id IS NOT NULL
    

    【讨论】:

    • 我认为你应该在这两种情况下都使用左连接。
    • 它似乎正在删除我从未记录过任何活动的会员。我得到与 Tomalak 的查询相同的结果。如果我将第二个 JOIN 更改为 LEFT JOIN,它似乎会返回所有活动记录。
    • @ShawnMcBride WHERE t1.id IS NOT NULL 不见了。请再试一次。
    【解决方案3】:
    Select max(a.id), m.name, a.activity_code, a.activity_timestamp, a.activity_description
    From members m
         Left join
         activities a on a.member_id=m.id
    Group by  m.name, a.activity_code, a.activity_timestamp, a.activity_description
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-05-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-04-11
      • 2021-01-30
      • 1970-01-01
      相关资源
      最近更新 更多