【问题标题】:Selecting where more than one value from a joined table matches a given set从连接表中选择多个值与给定集合匹配的位置
【发布时间】:2010-12-10 07:01:54
【问题描述】:

我正在使用 Postgresql。我有一个项目表、一个标签表和一个将许多标签链接到一个项目的表。我需要选择匹配 2 个(或更多)给定标签的项目。

我可以选择与WHERE tag.name IN ('tag1', 'tag2') 匹配的1 个或多个项目在MySQL 中我想我可以说WHERE x IN ALL (y, z),但这似乎不起作用是pgsql。

到目前为止,我最好的方法是使用子查询将两个表连接两次。这显然不会扩展到匹配许多标签,我敢肯定这不是最有效的方法。

SELECT * 
FROM item 
JOIN (SELECT item.id FROM item 
      JOIN tagged on tagged.item=item.id 
      JOIN tag ON tag.id=taged.tag 
      WHERE tag.name='tagOne') p ON p.id=item.id
JOIN tagged ON tagged.item=item.id
JOIN tag ON tag.id=tagged.tag
WHERE tag.name='tagTwo'`

编辑:我仍在测试,但在子查询中使用它比我上面的尝试效果更好

SELECT item.id, count(tag2) AS relevance
FROM item
JOIN tagged tagged1 ON tagged1.item=item.id
JOIN tag tag1 ON (tag1.id=tagged1.tag AND tag1.name='tag1')
JOIN tagged tagged2 ON tagged2.item=item.id
JOIN tag tag2 ON (tag2.id=tagged2.tag)
WHERE tag2.name IN ('tag2', 'tag3')
GROUP BY item.id

根据要求,这里有一些表定义以供澄清:

CREATE TABLE item (id serial, [...]);
CREATE TABLE tag (id serial, name string UNIQUE);
CREATE TABLE taged (tag int references tag(id), item int references item(id));

【问题讨论】:

  • 我不认为 WHERE x IN ALL (y, z) 做你在 MySQL 中的想法。您能否提供一个 MySQL 示例,以便我们准确了解您的想法?
  • 如果添加一些表定义会改善问题,因为表/列命名有点混乱

标签: sql postgresql join


【解决方案1】:

这里有 3 种(许多)可能的方法:

select * 
from item 
where id in ( select tagged.item
              from tagged join tag on(tag.id=taged.tag)
              where tag.name in('tagOne', 'tagTwo')
              group by tagged.item
              having count(*)=2 );

select * 
from item join ( select tagged.item, count(*) as numtags 
                 from tagged join tag on(tag.id=taged.tag)
                 where tag.name in('tagOne', 'tagTwo')
                 group by tagged.item ) using (id)
where numtags=2;

select *
from item
where id in ( select tagged.item
              from tagged join tag on(tag.id=taged.tag)
              where tag.name='tagOne'
              intersect
              select tagged.item
              from tagged join tag on(tag.id=taged.tag)
              where tag.name='tagTwo' );

如果您只想要 2 个或更多匹配项,但不介意匹配哪些标签:

select * 
from item 
where id in ( select item
              from tagged
              group by item
              having count(*)>=2 );

【讨论】:

  • 我会投票赞成,但我还没有 15 个代表。除非有更好的帖子,否则我将把这个打开一段时间并接受这个答案,谢谢。
  • 嗯,这真的有用吗?这个想法很漂亮,但将 ID 加入 ITEM 似乎并不正确 - 对于 ID IN ( item ...) 也是如此
  • @a_horse item 是表 tagged 的列名 - 请参阅问题中的 JOIN tagged on tagged.item=item.id 示例
  • 第四个例子最接近我所需要的 Jack,感谢这些。根据我的第二个示例,我将在别名下两次加入表,并在子查询中使用您的HAVING count(*) >=2。您看到的第一个标签必须匹配,只有 >=2 它会匹配 2,3 而不是 1。我应该在帖子中包含它。
【解决方案2】:

我不确定我是否理解,但也许你可以简单地写:

WHERE tag.name IN (y) AND tag.name IN (z)

【讨论】:

  • 返回零结果。针对 where 条件进行测试的行与两个条件都不匹配。
【解决方案3】:

我喜欢使用表格:

SELECT *
FROM item
WHERE EXISTS (SELECT 1 FROM tagged JOIN tag ON tag.id = tagged.tag
              WHERE tag.name = 'tag1')
      AND
      EXISTS (SELECT 1 FROM tagged JOIN tag ON tag.id = tagged.tag
              WHERE tag.name = 'tag2')

您断言“这显然无法扩展到匹配许多标签,我敢肯定这不是最有效的方法”——但这听起来像是您在猜测?

【讨论】:

  • 我认为 “不会扩展” 是指每当需要包含新标签时需要添加/更改的 SQL 数量
  • 我猜测效率,但是a_horse_with_no_name 是正确的,我希望能够用相同的SQL 匹配2 个或更多标签。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-02-23
  • 2017-09-05
  • 2019-09-07
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多