在多对多关系中使用“AND”

时间:2013-03-26 00:29:54

标签: sql sql-server sql-server-2008 tsql select

我有一个Users表和一个Groups表。用户可以通过“UserInGroup”表位于多个组中,而组可以具有“GroupTypeId”。

[User]
--------------
Id | Name
1  | Bob
2  | James

[UserInGroup]
-----------------
UserId | GroupId
1        1
1        2

[Group]
Id | Name      | TypeId
------------------------
1  | Directors | 1
2  | IT        | 1
3  | London    | 2

我想创建一个查询来返回例如“导演”和“伦敦”(而不是“导演”或“伦敦”)的用户。但是,我只想要AND组的不同'Type',我想要OR组相同的类型。我可以为每个组类型设置一个单独的表,但我不能动态创建它们。

理想情况下,我希望能够查询“董事”或“IT”和“伦敦”的用户。

最有效的方法是什么?

2 个答案:

答案 0 :(得分:3)

此问题通常称为Relational Division

SELECT  a.Name
FROM    [user] a
        INNER JOIN UserInGroup b
            ON a.ID = b.UserID
        INNER JOIN [Group] c
            ON b.groupID = c.TypeId
WHERE   c.Name IN ('Directors','London')
GROUP   BY a.Name
HAVING  COUNT(*) = 2

但如果UNIQUEGROUP每个USER强制实施DISTINCT约束,则需要SELECT a.Name FROM [user] a INNER JOIN UserInGroup b ON a.ID = b.UserID INNER JOIN [Group] c ON b.groupID = c.TypeId WHERE c.Name IN ('Directors','London') GROUP BY a.Name HAVING COUNT(DISTINCT c.Name) = 2 个关键字来过滤掉唯一群组:

╔══════╗
║ NAME ║
╠══════╣
║ Bob  ║
╚══════╝

来自两个查询的输出

{{1}}

答案 1 :(得分:0)

我得到了以下解决方案(在J W和this article的帮助下):

SELECT  
  u.Name UserName
FROM [User] u
INNER JOIN [UserInGroup] uig
  ON uig.UserId = u.Id
INNER JOIN [Group] g
  ON g.Id = uig.GroupId
WHERE 
  g.Id IN (1,2,3) -- these are the passed in groupids
GROUP BY 
  u.Name
having count(distinct g.TypeId)
  = (select count(distinct g1.TypeId) 
     from [group] g1 where g1.Id IN (1,2,3))

这允许我通过鉴别器字段对关系除法进行分组。另一种选择是:

SELECT  a.Name
FROM    [User] a
    INNER JOIN
    (
      SELECT  b.UserID
      FROM    UserInGroup b
              INNER JOIN [Group] c
                  ON b.groupID = c.Id
      WHERE   c.Name IN ('Directors','IT')
      GROUP   BY b.UserID
      HAVING  COUNT(DISTINCT c.Name) >= 1
    ) b ON a.ID = b.UserID
    INNER JOIN
    (
      SELECT  DISTINCT b.UserID
      FROM    UserInGroup b
              INNER JOIN [Group] c
                  ON b.groupID = c.Id
      WHERE   c.Name = 'London'
    ) c ON a.ID = c.UserID

为每个GroupTypeId添加额外的连接。执行计划看起来很相似,所以我选择了第一个选项。