【问题标题】:MySQL: How to use GROUP_CONCAT with WHERE comparisonMySQL:如何将 GROUP_CONCAT 与 WHERE 比较一起使用
【发布时间】:2020-05-06 19:28:48
【问题描述】:

我有 2 个表:groups:组名的简单列表,group_users:组及其用户的列表,例如:

group_id user_id
1        2
1        37
1        38
3        14
8        2
8        24
8        27

我有一个GROUP_CONCAT 查询当前正在运行:

SELECT g.group_id, g.groupname
,GROUP_CONCAT(DISTINCT CONVERT(gu.user_id, CHAR(2)) ORDER BY gu.user_id) AS group_members
FROM group_users gu, groups g
WHERE g.group_id = gu.group_id
GROUP BY gu.group_id
ORDER BY gu.group_id;

这会产生一个结果:

group_id  groupname  group_members
1         Sales      2,37,38
3         Marketing  3
8         Production 2,24,27

我的问题:我正在尝试获取特定用户 ID 所属的组列表(例如,此处,user_id 2 应该产生结果行 1 和 3)。

但是,当我更改条件 WHERE g.group_id = gu.group_id AND gu.user_id = 2 时,我得到:

group_id  groupname  group_members
1         Sales      2
8         Production 2

组已正确过滤,但group_members 列表仅限于所述 ID。

我看到许多 SO 答案建议使用 HAVINGWHERE,但这会产生错误。我试过JOINs,也不管用。

如何将groups_users 表过滤为只有user_id 为2 的表,然后获取group_ids 的所有成员的列表?

【问题讨论】:

    标签: mysql sql group-by where-clause group-concat


    【解决方案1】:

    您可以添加查询来过滤所需的组:

    SELECT g.group_id, g.groupname
    ,GROUP_CONCAT(DISTINCT CONVERT(gu.user_id, CHAR(2)) ORDER BY gu.user_id) AS group_members
    FROM (SELECT group_id FROM group_users WHERE user_id=2) gu1
    INNER JOIN group_users gu
    ON gu.group_id=gu1.group_id
    INNER JOIN groups g
    ON g.group_id = gu.group_id
    GROUP BY gu.group_id
    ORDER BY gu.group_id;
    

    【讨论】:

    • 创建临时表的子查询,然后添加 2 INNER JOINs。一个完美的解决方案,我不会很快想到。谢谢!
    【解决方案2】:

    一种选择是使用带有GROUP_CONCAT() 的子查询,该子查询按包含having 子句的group_id 列分组,以检查user_id=2 的存在:

    SELECT g.group_id, g.groupname, gu.group_members
      FROM `groups` g
      JOIN (SELECT GROUP_CONCAT(DISTINCT user_id ORDER BY user_id) 
                   AS group_members, group_id
              FROM `group_users` 
             GROUP BY group_id
            HAVING COUNT(CASE WHEN user_id = 2 THEN 1 END) > 0
            ) gu
        ON g.group_id = gu.group_id 
     ORDER BY gu.group_id;
    
    +----------+------------+--------------+     
    | group_id | groupname  | group_members| 
    +----------+------------+--------------+
    |  1       | Sales      | 2,37,38      |
    |  8       | Production | 2,24,27      |
    +----------+------------+--------------+
    

    使用重音 groups 作为表名,因为它是一个保留字。

    Demo

    【讨论】:

    • Points for ` groups ` - 这在 MySQL 8.x 中突然出现。
    【解决方案3】:

    您可以只引用 group_users 两次,不需要子查询:

    SELECT g.group_id, g.groupname
       , GROUP_CONCAT(DISTINCT CONVERT(gu.user_id, CHAR(2)) ORDER BY gu.user_id) AS group_members
    FROM group_users AS ug -- the specified User's groups
    INNER JOIN groups AS g ON ug.group_id = g.group_id
    INNER JOIN group_users AS gu ON g.group_id = gu.group_id
    WHERE ug.user_id = 2
    GROUP BY g.group_id
    ORDER BY g.group_id
    ;
    

    还请注意,此答案以及迄今为止的所有其他答案都将您的隐式“逗号”联接查询转换为具有显式联接的查询。这是因为逗号连接语法在很长一段时间内一直被认为是陈旧且难以维护的,并且在十多年前几乎已被显式 JOIN 语法取代,除了最简单的查询。

    【讨论】:

      【解决方案4】:

      条件:

      WHERE gu.user_id = 2
      

      在聚合之前删除行。
      相反,您可以使用 HAVING 子句:

      SELECT g.group_id, g.groupname
      ,GROUP_CONCAT(DISTINCT gu.user_id ORDER BY gu.user_id) AS group_members
      FROM group_users gu INNER JOIN groups g
      ON g.group_id = gu.group_id
      GROUP BY gu.group_id
      HAVING SUM(gu.user_id = 2) > 0
      ORDER BY gu.group_id;
      

      还有你为什么要将user_ids 转换为CHAR(2)
      如果user_id 大于 99,它将被截断。
      使用:

      DISTINCT gu.user_id
      

      GROUP_CONCAT() 内,MySql 会隐式转换整数值。

      【讨论】:

      • 啊,2个字符的限制……这是给小公司的。为了乐观,我应该调整为 4 :) 我需要某种转换,因为原生 GROUP_CONCAT 结果是 BLOB ('0X322...'),即使使用 DISTINCT
      猜你喜欢
      • 2014-08-18
      • 1970-01-01
      • 2014-04-13
      • 1970-01-01
      • 2019-01-09
      • 1970-01-01
      • 2015-03-18
      • 2015-01-21
      • 2016-07-03
      相关资源
      最近更新 更多