选择具有最大值的行数

时间:2017-07-31 21:46:07

标签: mysql group-by max

工作示例:http://sqlfiddle.com/#!9/80995/20

我有三个表,一个用户表,一个user_group表和一个链接表。

链接表包含用户添加到用户组的日期。我需要一个返回每组中当前用户数的查询。最近的日期决定了用户当前所在的组。

SELECT
  user_groups.name, 
  COUNT(l.name) AS ct, 
  GROUP_CONCAT(l.`name` separator  ", ") AS members 
FROM user_groups
  LEFT JOIN 
    (SELECT MAX(added), group_id, name FROM link LEFT JOIN users ON users.id = link.user_id GROUP BY user_id) l 
    ON l.group_id = user_groups.id
GROUP BY user_groups.id

我的问题是,我写的查询是否可以优化,或写得更好。

谢谢! 本

1 个答案:

答案 0 :(得分:2)

您的实际查询并未提供您想要的答案;至少,据我了解你的问题。 John 实际上在2017-01-05加入了第2组,但它出现在第1组(他于2017-01-01加入)中。另请注意,您缺少一个 Group 4

使用标准SQL,我认为下一个查询是您正在寻找的。查询中的注释应阐明每个部分正在做什么:

SELECT
    user_groups.name AS group_name, 
    COUNT(u.name) AS member_count, 
    group_concat(u.name separator ', ') AS members 
FROM 
    user_groups
    LEFT JOIN
    (
       SELECT * FROM 
       (-- For each user, find most recent date s/he got into a group
       SELECT 
           user_id AS the_user_id, MAX(added) AS last_added
       FROM 
           link 
       GROUP BY 
           the_user_id
       ) AS u_a
       -- Join back to the link table, so that the `group_id` can be retrieved
       JOIN link l2 ON l2.user_id = u_a.the_user_id AND l2.added = u_a.last_added
    ) AS most_recent_group ON most_recent_group.group_id = user_groups.id

    -- And get the users...
    LEFT JOIN users u ON u.id = most_recent_group.the_user_id
GROUP BY 
   user_groups.id, user_groups.name
ORDER BY
   user_groups.name ;

这可以在MySQL中以更紧凑的方式编写(滥用这一事实,在旧版本的MySQL中,它不遵循GROUP BY限制的SQL标准。

这就是你会得到的:

group_name | member_count | members       
:--------- | -----------: | :-------------
Group 1    |            2 | Mikie, Dominic
Group 2    |            2 | John, Paddy   
Group 3    |            0 | null          
Group 4    |            1 | Nellie        

dbfiddle here

请注意,如果您使用带有窗口函数的数据库(例如MariaDB 10.2),则可以简化此查询。然后,您可以使用:

SELECT
    user_groups.name AS group_name, 
    COUNT(u.name) AS member_count, 
    group_concat(u.name separator ', ') AS members 
FROM 
    user_groups
    LEFT JOIN
    (
        SELECT 
            user_id AS the_user_id, 
            last_value(group_id) OVER (PARTITION BY user_id ORDER BY added) AS group_id
        FROM
            link
        GROUP BY
            user_id
    ) AS most_recent_group ON most_recent_group.group_id = user_groups.id

    -- And get the users...
    LEFT JOIN users u ON u.id = most_recent_group.the_user_id
GROUP BY 
   user_groups.id, user_groups.name
ORDER BY
   user_groups.name ;

dbfiddle here