SQL:查询包含一组精确用户的组

时间:2018-05-18 16:08:26

标签: mysql sql

如果我有一个简单的多对多用户和组连接表,例如:

CREATE TABLE IF NOT EXISTS `users`  (
  `id` int(6) unsigned NOT NULL,
  `name` varchar(16) NOT NULL,
  PRIMARY KEY (`id`)
) DEFAULT CHARSET=utf8;

CREATE TABLE IF NOT EXISTS `user_group` (
  `user_id` int(6) unsigned NOT NULL,
  `group_id` int(6) unsigned NOT NULL,
  PRIMARY KEY (`user_id`, `group_id`)
) DEFAULT CHARSET=utf8;

CREATE TABLE IF NOT EXISTS `groups` (
  `id` int(6) unsigned NOT NULL,
  `name` varchar(16) NOT NULL,
  PRIMARY KEY (`id`)
) DEFAULT CHARSET=utf8;

INSERT INTO `users` (`id`, `name`) VALUES
  ('1', 'Michael'),
  ('2', 'Sarah'),
  ('3', 'Steven'),
  ('4', 'Jane');

INSERT INTO `groups` (`id`, `name`) VALUES
  ('1', 'M Names'),
  ('2', 'S Names'),
  ('3', 'J Names'),
  ('4', 'Men'),
  ('5', 'Women');

INSERT INTO `user_group` (`user_id`, `group_id`) VALUES
  ('1', '1'),
  ('1', '4'),
  ('2', '2'),
  ('2', '5'),
  ('3', '2'),
  ('3', '4'),
  ('4', '3'),
  ('4', '5');

我试图弄清楚如何查看给定的一组用户是否构成现有组。

例如,如果我查询Michael,则返回组M Names,因为该组完全由Michael组成。

如果我查询(Sarah,Steven),它会返回组'S Names'。

如果我查询(Sarah,Michael),则不会返回任何组。

可以轻松查询给定用户组所在的组:

SELECT * FROM `user_group`
WHERE `user_id` in ('2', '3');

但是我不知道如何将其限制为只有所有给定用户都是其成员的组。

2 个答案:

答案 0 :(得分:2)

(更新,添加一些东西,使其不仅仅是一个子集,而是完全匹配)

我假设你想要所有用户都在一起的组。或者用户是子集的地方。以下查询将完成工作(除非我犯了错误)

SELECT g.Name
FROM groups g
INNER JOIN user_group ug on (ug.group_id=g.id)
WHERE ug.user_id IN (1,4)
GROUP BY g.id
HAVING COUNT(ug.user_id) = 2

解释:首先,您基本上(可能)希望按user_group过滤user_id以查找包含任何用户ID(WHERE - 子句)的组,然后您想要选择具有所有用户ID(GROUPHAVING - 子句)的组。

但是,此查询有两个不同的部分:where子句中的用户ID(在本例中为1,4)和having子句中的user_ids计数(在本例中为2)。

<强>更新

现在,由于您的帖子建议您想要完全匹配,因此您可以添加以下内容HAVING -

   AND COUNT(ug.user_id) = (SELECT COUNT(*) FROM user_group ug2 WHERE ug2.group_id=g.id)

确保该组的用户数与查询用户数相同。

答案 1 :(得分:2)

尝试此查询(适用于MySql 8):

WITH usr AS (
 SELECT 'Sarah' usr_name
 UNION
 SELECT 'Steven'
),
usr_id AS (
  SELECT * FROM usr 
  LEFT JOIN users u  ON u.name = usr.usr_name
)
SELECT * FROM (
  SELECT group_id
  FROM user_group ug
  LEFT JOIN usr_id u ON ug.user_id = u.id
  GROUP BY group_id
  HAVING count(*) = count(id)
     AND count(*) = (SELECT count(*) FROM usr)
) qq
JOIN `groups` g ON g.id = qq.group_id

工作演示:https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=7621f99e0b2d0bf1ec6fbfdc55424c48

WITH usr AS (
 SELECT 'Sarah' usr_name
 UNION
 SELECT 'Steven'
),
usr_id AS (
  SELECT * FROM usr 
  LEFT JOIN users u  ON u.name = usr.usr_name
)
SELECT * FROM (
  SELECT group_id
  FROM user_group ug
  LEFT JOIN usr_id u ON ug.user_id = u.id
  GROUP BY group_id
  HAVING count(*) = count(id)
     AND count(*) = (SELECT count(*) FROM usr)
) qq
JOIN `groups` g ON g.id = qq.group_id
group_id | id | name   
-------: | -: | :------
       2 |  2 | S Names
WITH usr AS (
 SELECT 'Michael' usr_name
),
usr_id AS (
  SELECT * FROM usr 
  LEFT JOIN users u  ON u.name = usr.usr_name
)
SELECT * FROM (
  SELECT group_id
  FROM user_group ug
  LEFT JOIN usr_id u ON ug.user_id = u.id
  GROUP BY group_id
  HAVING count(*) = count(id)
     AND count(*) = (SELECT count(*) FROM usr)
) qq
JOIN `groups` g ON g.id = qq.group_id
group_id | id | name   
-------: | -: | :------
       1 |  1 | M Names
WITH usr AS (
 SELECT 'Michael' usr_name
 UNION
 SELECT 'Sarah'
),
usr_id AS (
  SELECT * FROM usr 
  LEFT JOIN users u  ON u.name = usr.usr_name
)
SELECT * FROM (
  SELECT group_id
  FROM user_group ug
  LEFT JOIN usr_id u ON ug.user_id = u.id
  GROUP BY group_id
  HAVING count(*) = count(id)
     AND count(*) = (SELECT count(*) FROM usr)
) qq
JOIN `groups` g ON g.id = qq.group_id
group_id | id | name
-------: | -: | :---

db&lt;&gt;小提琴here