跨3个表的SQL连接,具有多个WHERE子句匹配

时间:2015-07-17 01:36:34

标签: sql postgresql join

我有三个表:user,user_tag和tag。其中最基本的要素转载如下。

用户使用中间user_tag表链接到标签。每个用户都可以拥有零个或多个标签。我很想找到那些拥有一个或多个匹配标签的用户。

用户

   Column    |              Type              |            Modifiers
-------------+--------------------------------+---------------------------------
 id          | integer                        | not null
 name        | character varying(150)         | not null

user_tag

   Column   |              Type              | Modifiers
------------+--------------------------------+-----------
 id         | integer                        | not null
 user_id    | integer                        |
 tag_id     | integer                        |

标签

   Column    |              Type              |            Modifiers
-------------+--------------------------------+---------------------------------
 id          | integer                        | not null
 name        | character varying(64)          | not null

因此,查找具有单个标记的用户很简单:

select u.id,u.name,g.name 
from user u 
join user_tag t on t.user_id = u.id 
join tag g on g.id = t.tag_id 
where g.name='TAGX';

我的问题是,如何匹配两个或更多标签?

做类似以下的事情并不起作用。

select u.id,u.name,g.name 
from user u 
join user_tag t on t.user_id = u.id 
join tag g on g.id = t.tag_id 
where (g.name='TAGX' and g.name='TAGY');

感觉我需要进行第二次加入来匹配第二个标签......?

2 个答案:

答案 0 :(得分:3)

首先,改变你的状况:

where (g.name='TAGX' and g.name='TAGY')

为:

where (g.name='TAGX' OR g.name='TAGY')

或:

where g.name in ('TAGX', 'TAGY')

您想要标记TAGXTAGY

的并集

现在你的输出应该是这样的:

+----+--------+------+
| ID |  Name  | Tag  |
+----+--------+------+
|  1 | User 1 | TAGX |
|  1 | User 1 | TAGY |
|  3 | User 3 | TAGX |
|  4 | User 4 | TAGY |
+----+--------+------+

正如您所提到的,您只想要拥有2个或更多标签的用户,而用户3和4在结果中是入侵者。 为了获得它们,你将不得不:

  • 从select statement
  • 中删除标记列
  • 按ID和名称分组用户
  • 计算每个用户拥有的代码数量
  • 创建条件以过滤少于2个标签的用户

像这样:

select u.id,u.name
from user u 
join user_tag t on t.user_id = u.id 
join tag g on g.id = t.tag_id 
where g.name in ('TAGX', 'TAGY')
group by u.id,u.name
having count(u.id) < 2; 

你的输出应该是:

+----+--------+
| ID |  Name  |
+----+--------+
|  1 | User 1 |
+----+--------+

如果要检查条件是否正确过滤,可以通过显示计数列并删除HAVING子句来进行可视验证。 像这样:

select u.id,u.name, count(u.id)
from user u 
join user_tag t on t.user_id = u.id 
join tag g on g.id = t.tag_id 
where g.name in ('TAGX', 'TAGY')
group by u.id,u.name;

我们应该告诉你:

+----+--------+-------+
| ID |  Name  | count |
+----+--------+-------+
|  1 | User 1 |     2 |
|  3 | User 3 |     1 |
|  4 | User 4 |     1 |
+----+--------+-------+

答案 1 :(得分:2)

如果您想查找具有这两个标记中的任何一个的用户,那么Tarik的答案将按照您的意愿执行,但如果您要查找同时包含这两个标记的用户(可能还有其他标记),则此查询将执行此操作:

select u.id, u.name
from user u 
join user_tag t on t.user_id = u.id 
join tag g on g.id = t.tag_id 
where g.name in ('TAGX', 'TAGY')
group by u.id, u.name
having count(distinct g.name) = 2; 

上面的查询将返回至少包含标签TAGX和TAGY的用户,但可以包含更多标签。如果你想要拥有这两个标签的用户,那么一个解决方案就是做一个相关的不存在的查询,如下所示:

select u.id, u.name, g.name
from user u 
join user_tag t on t.user_id = u.id 
join tag g on g.id = t.tag_id 
where not exists (
    select 1 
    from user_tag join tag on user_tag.tag_id = tag.id
    where tag.name not in ('TAGX', 'TAGY') 
    and user_tag.user_id = u.id
)