Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/opencv/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
SQL多对多筛选器仅用于特定的数据组合_Sql - Fatal编程技术网

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标准的版本,将MySQL
IF()
函数替换为合适的
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;