SQL多对多筛选器仅用于特定的数据组合
我有一个包含以下数据的多对多映射表SQL多对多筛选器仅用于特定的数据组合,sql,Sql,我有一个包含以下数据的多对多映射表 ID Person Role ----------------------------------------------------- 1 P1 R1 2 P1 R2 3 P1 R3 4 P2 R1 5 P2
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
需要考虑的事情
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 |
+--------+---+------+
此查询应适用于以下情况:
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;
假设(person,role)
tuple在表中是唯一的,我们可以这样做:
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标准的版本,将MySQLIF()
函数替换为合适的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)
我只是排除了您不想要的
角色
:
SELECT P.PERSON
FROM RMS.PERSONROLE P
WHERE NOT EXISTS (SELECT 1
FROM RMS.PERSONROLE
WHERE Person = P.Person AND role IN ('R4', 'R5')
);
但是,这可能无法提供您想要的结果。因此,我将使用groupby
子句
SELECT PERSON
FROM RMS.PERSONROLE
WHERE role IN ('R1', 'R2','R3')
GROUP BY PERSON
HAVING COUNT(DISTINCT ROLE) = 3;
这是一种丑陋的方式。我看不出额外(和不必要的)选择有什么好处。作为子查询或内联视图。使用
不在(子查询)
模式时要非常小心,注意当子查询返回空值时会发生什么。(OP没有保证person
列永远不会为空,)就MySQL的性能而言,我希望一个执行计划在没有子查询和具体化派生表的情况下通过表,效率会更高。我可能会对丑陋(adj)同义词unattractive使用合适的同义词,没有吸引力,…为了将来的参考,您不应该垃圾邮件DBMS标记。只放你实际使用的那一行,因为解决方案可能会有所不同。你是我见过的第一个像我一样为GROUP BY添加新行以保持正确的理由清晰的人。你真好。@Error\u 2646:我不明白为什么每个人都不这么做。(我最好的猜测是,他们希望在写完SQL之后再也不用读它了。或者也许他们比我聪明得多。)+10。在保证(person,role)元组的非空值和唯一性的情况下。。。此查询将列出R1、R2和R3中的人员,但不要求他们仅在这些区域中groups@andomar-这就是为什么它是值得思考的;-)这两个查询都不会列出且仅列出R1、R2和R3中的人员
SELECT PERSON
FROM RMS.PERSONROLE
WHERE role IN ('R1', 'R2','R3')
GROUP BY PERSON
HAVING COUNT(DISTINCT ROLE) = 3;