SQL多对多只过滤特定的数据组合

时间:2018-05-15 18:36:55

标签: sql

我有一个包含以下数据的多对多映射表。

ID       Person             Role
-----------------------------------------------------
1          P1                R1
2          P1                R2
3          P1                R3
4          P2                R1
5          P2                R3
6          P2                R5
7          P3                R3
8          P4                R1
9          P4                R2
10         P4                R3
11         P4                R4
12         P4                R5
13         P5                R1
14         P5                R2
15         P5                R3

我想只过滤角色R1,R2,R3的人。具有唯一角色R1,R2,R3的正确人员是P1和P5。

以下查询也会返回角色为R1,R2,R3,R4的人。

 SELECT PERSON 
   FROM RMS.PERSONROLE
  WHERE role IN ('R1', 'R2','R3') 
  GROUP   
     BY PERSON HAVING COUNT(ROLE)=3;

Expected Output
----------------------
Person
----------------------         
P1
P5    

5 个答案:

答案 0 :(得分:1)

select  Person
from    Table1
group by
        Person
having  group_concat(role order by role) = 'R1,R2,R3'

Example at SQL Fiddle.

答案 1 :(得分:1)

要考虑的事情......

DROP TABLE IF EXISTS my_table;

CREATE TABLE my_table
(person INT NOT NULL
,role INT NOT NULL
,PRIMARY KEY(person,role)
);

INSERT INTO my_table VALUES
(1,101),
(1,102),
(1,103),
(2,101),
(2,103),
(2,105),
(3,103),
(4,101),
(4,102),
(4,103),
(4,104),
(4,105),
(5,101),
(5,102),
(5,103);

SELECT person
     , COUNT(*)x
     , SUM(role IN(101,102,103))y 
  FROM my_table 
 GROUP 
    BY person;
+--------+---+------+
| person | x | y    |
+--------+---+------+
|      1 | 3 |    3 |
|      2 | 3 |    2 |
|      3 | 1 |    1 |
|      4 | 5 |    3 |
|      5 | 3 |    3 |
+--------+---+------+

答案 2 :(得分:1)

假设(person,role)元组在表中是唯一的,我们可以这样做:

 SELECT p.PERSON 
   FROM RMS.PERSONROLE p
  GROUP   
     BY p.PERSON
 HAVING 3 = SUM(IF(p.role IN ('R1','R2','R3'),1,0)
    AND 3 = SUM(1)

如果没有唯一性的保证,我们可以稍微调整一下来计算不同的角色值

 SELECT p.PERSON 
   FROM RMS.PERSONROLE p
  GROUP   
     BY p.PERSON
 HAVING 3 = COUNT(DISTINCT IF(p.role IN ('R1','R2','R3'),p.role,NULL))
    AND 3 = COUNT(DISTINCT p.role)

修改

上面的答案是针对MySQL的。一个更易于移植的ANSI标准兼容版本,用适当的IF()表达式替换MySQL CASE函数。

 SELECT p.PERSON 
   FROM RMS.PERSONROLE p
  GROUP   
     BY p.PERSON
 HAVING 3 = COUNT(DISTINCT CASE WHEN p.role IN ('R1','R2','R3') THEN p.role END)
    AND 3 = COUNT(DISTINCT p.role)

答案 3 :(得分:0)

此查询应该有效:

SELECT PERSON 
FROM PERSONROLE
WHERE PERSON NOT IN 
(
    SELECT PERSON FROM PERSONROLE WHERE role NOT IN('R1', 'R2','R3')
)  
GROUP BY PERSON HAVING COUNT(ROLE)=3;  

或者这可能更好

SELECT p.PERSON FROM PERSONROLE p 
LEFT JOIN
(
    SELECT PERSON FROM PERSONROLE WHERE role NOT IN('R1', 'R2','R3')
) a on  p.PERSON = a.person
WHERE a.person is null
GROUP BY PERSON HAVING COUNT(ROLE)=3; 

答案 4 :(得分:0)

我只是排除了您不想要的roles

SELECT P.PERSON 
FROM RMS.PERSONROLE P
WHERE NOT EXISTS (SELECT 1 
                   FROM RMS.PERSONROLE 
                   WHERE Person = P.Person AND role IN ('R4', 'R5')
                  );

但是,这可能无法提供您想要的结果。所以,我会选择GROUP BY条款

SELECT PERSON 
FROM RMS.PERSONROLE
WHERE role IN ('R1', 'R2','R3') 
GROUP BY PERSON 
HAVING COUNT(DISTINCT ROLE) = 3;