SQL: запрос для группы, которая содержит точный набор пользователей - PullRequest
0 голосов
/ 18 мая 2018

Если у меня есть простая таблица соединений пользователей и групп «многие ко многим», например:

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');

Я пытаюсь выяснить, как определить, составляет ли данный набор пользователей существующийгруппа.

Например, если я запрашиваю Майкла, он возвращает группу M Names, потому что эта группа целиком состоит из Майкла.

Если я запрашиваю (Сара, Стивен), она возвращает группу'S Names'.

Если я запрашиваю (Сара, Майкл), группа не возвращается.

Запросить группы, в которых находится данный набор пользователей, легко:

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

Но я не знаю, как ограничить это только группами, членами которых являются все указанные пользователи.

Ответы [ 2 ]

0 голосов
/ 18 мая 2018

Попробуйте этот запрос (работает на 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
-------: | -: | :---

дБ <> скрипка здесь

0 голосов
/ 18 мая 2018

(обновлено, добавьте что-нибудь, чтобы сделать это не просто подмножеством, а точным соответствием)

Я предполагаю, что вам нужны группы, членами которых являются все пользователи.или где пользователи являются подмножеством.следующий запрос выполнит работу (если я не ошибся)

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, чтобы найти группы, содержащие ЛЮБОГО из пользователей'ids (WHERE -clause), затем вы хотите выбрать группы, которые имеют ВСЕ идентификаторы пользователей (GROUP и HAVING -clause).

Однако этот запрос состоит из двух различных частей: идентификаторы пользователя в предложении where (в данном случае 1,4) и количество этих user_ids в предложении Имеющий (в данном случае 2).

обновление

Теперь, так как ваше сообщение предлагает вам точное совпадение, вы можете добавить следующее к HAVING -классе

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

, который гарантирует, что количество пользователей этой группы совпадает с количеством пользователей запроса.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...