如何通过透视表选择具有特定关系的行?

时间:2018-10-16 13:50:00

标签: mysql sql

样本数据库设计

  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. 像John这样的人,一周的所有工作日完全:星期一,星期二,星期三,星期四,星期五
  2. 像哈利(Harry)和约翰(John)这样的人,至少在这些日子里至少工作

我在一段时间内使用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 :(得分:1)

  • 您可以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

对于第二种情况,只需删除sundaysaturday上的条件。试试:

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

答案 1 :(得分:1)

一整天之后,我都会做:

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

答案 2 :(得分:1)

如果您希望在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 |

请注意,我为此使用了自己的用户表