用户,User_Group和订阅表之间的高级内部联接

时间:2016-01-12 21:22:05

标签: mysql sql database select inner-join

我有三张桌子:

Users          { id | name | email }
User_Group     { User_id | group_id }
Subscriptions  {user_id, sub_level }
  • 用户可以签署更多该用户组。
  • 用户可以订阅超过订阅计划。

假设我们有:

表用户:

[1, John, john@email.com]

[2, Lara, lara@email.com]

表用户组:

[1,6]  // User 1 is assigned to Group 6

[1,3]  // User 1 also assigned to Group 3

[2,3]  // User 2 in ONLY assigned to Group 3 

表订阅:

[1, 8] // User 1 have subscription level 8

[2, 8] // User 2 have subscription level 8

[2, 9] // Also User 2 have subscription level 9

我想得到的是:

仅分配给第3组并且至少拥有一个订阅的所有UNIQUE用户。

我尝试过:

SELECT U.Username, U.email, G.group_id, S.sub_level FROM `Users` AS U
INNER JOIN `User_Group` AS G
ON U.id = G.user_id
INNER JOIN `Subscriptions` AS S
ON U.id = S.user_id
WHERE G.group_id = 3
Limit 0,10

问题是它会显示同时分配给第6组和第3组中的用户1等其他组的用户。此外,它还会显示重复的行,因为一个用户可以拥有多个订阅级别。

3 个答案:

答案 0 :(得分:2)

一种方法是使用聚合:

SELECT U.Username, U.email, MAX(G.group_id)
FROM `Users` U INNER JOIN
     `User_Group` G
     ON U.id = G.user_id INNER JOIN
     `Subscriptions` S
     ON U.id = S.user_id
GROUP BY  U.Username, U.email
HAVING MAX(G.group_id) = 3 AND MIN(G.group_id) = 3 ; -- condition on groups

订阅条件仅由join条件处理。

这可能更有效:

select u.*
from users u
where exists (select 1
              from user_groups ug
              where ug.user_id = u.id and ug.group_id = 3
             ) and
      not exists (select 1
                  from user_groups ug
                  where ug.user_id = u.id and ug.group_id <> 3
                 ) and
      exists (select 1
              from subscriptions s
              where s.user_id = u.id
             );

对于此查询,您需要user_groups(user_id, group_id)subscriptions(user_id)上的索引。实际上,这两个索引对于制定查询的两种方式都是一个好主意。

答案 1 :(得分:1)

SELECT
    U.username, U.email,
    G.group_id,
    S.sub_level
FROM
    Users U
INNER JOIN User_Group G ON
    G.user_id = u.id AND
    G.group_id = 3
INNER JOIN Subscriptions S ON S.user_id = U.id
WHERE
    NOT EXISTS
    (
        SELECT *
        FROM User_Group G2
        WHERE G2.user_id = U.user_id AND G2.group_id <> 3
    )

答案 2 :(得分:1)

尝试:

SELECT U.Username, U.email FROM `Users` AS U
INNER JOIN `User_Group` AS G
ON U.id = G.user_id
INNER JOIN `Subscriptions` AS S
ON U.id = S.user_id
WHERE G.group_id = 3
GROUP BY U.UserName, U.EMail

这会将用户加入所有订阅,然后加入用户组3;然后聚合结果,使每个用户只出现一次。

你的原始样本在输出中显示了Group_ID,但它总是为3.它在输出中也显示了Sub_Level,但这没有意义,因为你想要每个用户只有一行而不管Subcription条目 - 会是什么值你想看?

如果您真的想在结果集中使用这两列,您可以这样做:

SELECT U.Username, U.email, G.Group_ID, COUNT(S.Sub_Level) FROM `Users` AS U
INNER JOIN `User_Group` AS G
ON U.id = G.user_id
INNER JOIN `Subscriptions` AS S
ON U.id = S.user_id
WHERE G.group_id = 3
GROUP BY U.UserName, U.EMail, G.Group_ID

将为组级别提供3,并告诉您有多少订阅记录被&#34;压缩&#34;进入每个结果行。代替COUNT,您也可以使用MIN或MAX或(因为这是MySQL)GROUP_CONCAT函数,它将为您提供以逗号分隔的订阅级别的字符串列表。