SQL:选择包含确切给定成员的集合

时间:2012-10-08 06:12:44

标签: sql

我确信有一个适当的词语,我没记错,但问题很容易描述: 我有一个表组成员,这是组和成员之间的简单关系:

id | groupid | memberid
1  | g1      | m1
2  | g1      | m2
3  | g2      | m1
4  | g2      | m2
5  | g2      | m3

上面描述了两组,一组是m1和m2,一组是m1,m2和m3。 如果我想选择有成员m1,m2但没有其他成员的groupids,我该怎么做?我尝试过的方法也会返回g2,因为m1和m2是它们的子集。

更新:哇,一些很棒的答案!让我首先澄清一下我的问题 - 我希望能够选择与给定成员m1和m2完全匹配的组。因此,如果该组还包含的成员数多于m1和m2,则它不应匹配,如果该组包含的成员数少于m1和m2,则不应匹配。

6 个答案:

答案 0 :(得分:9)

来自您的短语

  

我想选择有成员m1,m2但没有其他成员的groupids

试试这个,背后的想法是count与条件和where子句匹配的记录的总实例,并且它等于每个组的记录总数。

SELECT groupid
FROM table1 a
WHERE memberid IN ('m1','m2')
GROUP BY groupid
HAVING COUNT(*) = 
(
  SELECT COUNT(*)
  FROM table1 b
  WHERE b.groupid = a.groupid
  GROUP BY b.groupID
)

SQLFiddle Demo

答案 1 :(得分:4)

您正在寻找具有m1和m2的组与具有正好两个成员的组之间的交集。 SQL有一个运算符:

select groupid
from group_table
where memberid in ('m1','m2')
group by groupid 
having count(distinct memberid) = 2
intersect
select groupid 
from group_table
group by groupid 
having count(distinct memberid) = 2

(如果您使用的是Oracle,intersect称为minus

这是一个SQLFiddle演示:http://sqlfiddle.com/#!12/df94d/1

虽然我认为John Woo的解决方案在性能方面可能更有效率。

答案 2 :(得分:1)

-- sample table for discussion
CREATE TABLE tbl
  (id int, groupid varchar(2), memberid varchar(2));
INSERT INTO tbl
  (id, groupid, memberid)
VALUES
    (6, 'g4', 'm1'),
    (7, 'g4', 'm2'),
    (8, 'g6', 'm1'),
    (9, 'g6', 'm3'),
    (1, 'g1', 'm1'),
    (2, 'g1', 'm2'),
    (3, 'g2', 'm1'),
    (4, 'g2', 'm2'),
    (5, 'g2', 'm3')
;

-- the query
select a.groupid, b.groupid peer
from (select groupid, count(*) member_count, min(memberid) x, max(memberid) y
      from tbl
      group by groupid) A
join
     (select groupid, count(*) member_count, min(memberid) x, max(memberid) y
      from tbl
      group by groupid) B
  on a.groupid<b.groupid and a.member_count=b.member_count and a.x=b.x and a.y=b.y
join tbl A1
  on A1.groupid = A.groupid
join tbl B1
  on B1.groupid = B.groupid and A1.memberid = B1.memberid
group by A.groupid, b.groupid, A.member_count
having count(1) = A.member_count;

-- the result
GROUPID PEER
g1  g4

以上显示了一种以非常优化的方式将groups列入其同行的方法。通过将组分解为成员计数并使用最小值和最大值,它可以很好地处理大型数据库。使用直接连接快速减少这些组,并且仅对剩余的匹配是完整的表,参考组A和B加入,以最终确定它们是否是等效的组。

如果你有3个相似的组(101,103,104),这些组将显示为三个单独的行(101,103),(101,104),(103,104) - 因为每对形成一个对等,所以如果你已经这样的话,最好使用这样的查询知道你想找到同伴的其中一个小组。此过滤器适合第一个子查询。

答案 3 :(得分:1)

SELECT DISTINCT                          -- if (groupid, memberid) is unique
                                         -- no need for the DISTINCT
    a.groupid
FROM 
    tableX AS a
  JOIN
    tableX AS b  
      ON b.groupid = a.groupid 
WHERE a.memberid = 'm1' 
  AND b.memberid = 'm2'
  AND NOT EXISTS
      ( SELECT *
        FROM tableX AS t
        WHERE t.groupid = a.groupid
          AND t.memberid NOT IN ('m1', 'm2') 
      ) ;

答案 4 :(得分:1)

此查询存在问题

SELECT groupid
FROM table1 a
WHERE memberid IN ('m1','m2')
GROUP BY groupid
HAVING COUNT(*) = 
(
  SELECT COUNT(*)
  FROM table1 b
  WHERE b.groupid = a.groupid
  GROUP BY b.groupID
)

它将仅匹配m1组或m2组。 为此我们可以添加另一个计数检查

SELECT groupid
FROM table1 a
WHERE memberid IN ('m1','m2')
GROUP BY groupid
HAVING COUNT(*) = 2 --since we already know we should have exactly two rows
AND COUNT(*) = 
(
  SELECT COUNT(*)
  FROM table1 b
  WHERE b.groupid = a.groupid
  GROUP BY b.groupID
)

答案 5 :(得分:0)

id | groupid | memberid
1  | g1      | m1
2  | g1      | m2
3  | g2      | m1
4  | g2      | m2
5  | g2      | m3

select GRPID from arcv where GRPID in (
select GRPID from arcv  
group by GRPID  having count(1)=2) and memberid in ('m1','m2')