Как выбрать строки, которые имеют определенные отношения через сводную таблицу? - PullRequest
0 голосов
/ 16 октября 2018

Пример базы данных

  USERS            WORKDAYS                DAYS
[id] [name]      [user_id] [day_id]      [id] [name]
 1    john        1         2             1    sunday
 2    fred        1         3             2    monday
 3    bert        1         4             3    tuesday
 4    harry       1         5             4    wednesday
                  1         6             5    thursday
                  2         2             6    friday
                  4         1             7    saturday
                  4         2
                  4         3
                  4         4
                  4         5
                  4         6
                  4         7

Как бы я запросил

  1. Кто-то вроде Джона, который работает точно все рабочие дни недели: понедельник, вторникСреда, четверг, пятница
  2. Кто-то вроде Гарри и Джона, который работает хотя бы все эти дни?

Я работаю с MySQL некоторое время, но не могу найтирешение на данный момент.Обычно я бы использовал битовый флаг для этого, но я пытаюсь понять более нормализованное решение в SQL.

CREATE TABLE `users` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(100) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;

CREATE TABLE `days` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(100) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8;

CREATE TABLE `workdays` (
  `user_id` int(11) NOT NULL,
  `day_id` int(11) NOT NULL,
  KEY `workdays_users_FK` (`user_id`),
  KEY `workdays_days_FK` (`day_id`),
  CONSTRAINT `workdays_days_FK` FOREIGN KEY (`day_id`) REFERENCES `days` (`id`),
  CONSTRAINT `workdays_users_FK` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;



INSERT INTO play.users
(id, name)
VALUES(1, 'john');
INSERT INTO play.users
(id, name)
VALUES(2, 'fred');
INSERT INTO play.users
(id, name)
VALUES(3, 'bert');
INSERT INTO play.users
(id, name)
VALUES(4, 'harry');

INSERT INTO play.days
(id, name)
VALUES(1, 'sunday');
INSERT INTO play.days
(id, name)
VALUES(2, 'monday');
INSERT INTO play.days
(id, name)
VALUES(3, 'tuesday');
INSERT INTO play.days
(id, name)
VALUES(4, 'wednesday');
INSERT INTO play.days
(id, name)
VALUES(5, 'thursday');
INSERT INTO play.days
(id, name)
VALUES(6, 'friday');
INSERT INTO play.days
(id, name)
VALUES(7, 'saturday');

INSERT INTO play.workdays
(user_id, day_id)
VALUES(1, 2);
INSERT INTO play.workdays
(user_id, day_id)
VALUES(1, 3);
INSERT INTO play.workdays
(user_id, day_id)
VALUES(1, 4);
INSERT INTO play.workdays
(user_id, day_id)
VALUES(1, 5);
INSERT INTO play.workdays
(user_id, day_id)
VALUES(1, 6);
INSERT INTO play.workdays
(user_id, day_id)
VALUES(2, 2);
INSERT INTO play.workdays
(user_id, day_id)
VALUES(4, 1);
INSERT INTO play.workdays
(user_id, day_id)
VALUES(4, 2);
INSERT INTO play.workdays
(user_id, day_id)
VALUES(4, 3);
INSERT INTO play.workdays
(user_id, day_id)
VALUES(4, 4);
INSERT INTO play.workdays
(user_id, day_id)
VALUES(4, 5);
INSERT INTO play.workdays
(user_id, day_id)
VALUES(4, 6);
INSERT INTO play.workdays
(user_id, day_id)
VALUES(4, 7);

Ответы [ 3 ]

0 голосов
/ 16 октября 2018

Для всех дней, а затем и для некоторых, я бы сделал:

SELECT u.id, u.name
FROM USERS u JOIN
     WORKDAYS wd
     ON wd.user_id = u.id JOIN
     DAYS d
     ON d.id = wd.day_id 
GROUP BY u.id, u.name 
HAVING SUM(d.name = 'monday') > 0 AND
       SUM(d.name = 'tuesday') > 0 AND 
       SUM(d.name = 'wednesday') > 0 AND
       SUM(d.name = 'thursday') > 0 AND
       SUM(d.name = 'friday') > 0;

Или:

SELECT u.id, u.name
FROM USERS u JOIN
     WORKDAYS wd
     ON wd.user_id = u.id JOIN
     DAYS d
     ON d.id = wd.day_id 
WHERE d.name IN ('monday', 'tuesday', 'wednesday', 'thursday', 'friday')
GROUP BY u.id, u.name 
HAVING COUNT(DISTINCT d.name) = 5

Для ровно тех дней и никаких других:

SELECT u.id, u.name
FROM USERS u JOIN
     WORKDAYS wd
     ON wd.user_id = u.id JOIN
     DAYS d
     ON d.id = wd.day_id 
GROUP BY u.id, u.name 
HAVING SUM(d.name = 'monday') > 0 AND
       SUM(d.name = 'tuesday') > 0 AND 
       SUM(d.name = 'wednesday') > 0 AND
       SUM(d.name = 'thursday') > 0 AND
       SUM(d.name = 'friday') > 0 AND
       SUM(d.name NOT IN ('monday', 'tuesday', 'wednesday', 'thursday', 'friday')) = 0
0 голосов
/ 16 октября 2018

Если вы хотите, чтобы в 1 запросе вы могли

select
    id,
    name,
    group_concat(distinct w.day_id order by w.day_id) worked
from
    users u
join workdays w on
    u.id = w.user_id
group by
    id,
    name
having
    worked like '%2,3,4,5,6%' // or worked ='2,3,4,5,6'

id |name  |worked        |
---|------|--------------|
1  |john  |2,3,4,5,6     |
4  |harry |1,2,3,4,5,6,7 |

примечание, я использовал свою собственную таблицу пользователя для этого

0 голосов
/ 16 октября 2018
  • Вы можете GROUP BY для идентификатора и имени пользователя.
  • Использовать предложение HAVING с агрегацией SUM() для фильтрации случаев.
  • Если пользователь не работаетв определенный день SUM() для этого дня будет равно нулю, присоединение к записи.

Для первого случая (работает точно все рабочие дни недели), Попробуйте:

SELECT u.id, u.name
FROM USERS AS u 
JOIN WORKDAYS AS wd ON wd.user_id = u.id 
JOIN DAYS AS d ON d.id = wd.day_id 
GROUP BY u.id, u.name 
HAVING SUM(d.name = 'monday') 
   AND SUM(d.name = 'tuesday') 
   AND SUM(d.name = 'wednesday') 
   AND SUM(d.name = 'thursday') 
   AND SUM(d.name = 'friday') 
   AND SUM(d.name = 'sunday') = 0 
   AND SUM(d.name = 'saturday') = 0

Для второго случая просто удалите условия для sunday и saturday.Попробуйте:

SELECT u.id, u.name
FROM USERS AS u 
JOIN WORKDAYS AS wd ON wd.user_id = u.id 
JOIN DAYS AS d ON d.id = wd.day_id 
GROUP BY u.id, u.name 
HAVING SUM(d.name = 'monday') 
   AND SUM(d.name = 'tuesday') 
   AND SUM(d.name = 'wednesday') 
   AND SUM(d.name = 'thursday') 
   AND SUM(d.name = 'friday') 
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...