选择没有特定标签的帖子

Posted

技术标签:

【中文标题】选择没有特定标签的帖子【英文标题】:Select a post that does not have a particular tag 【发布时间】:2012-09-15 18:06:08 【问题描述】:

我有一个 post/tag 数据库,其中包含通常的 post、tag 和 tag_post 表。 tag_post 表包含 tagid 和 postid 字段。

我需要查询帖子。当我想获取具有特定标签的帖子时,我必须使用连接:

... INNER JOIN tag_post ON post.id = tag_post.postid 
WHERE tag_post.tagid = required_tagid`

当我想获取包含 tagIdA 和 tagIdB 的帖子时,我必须使用两个连接(我最终同意了)。

现在,我需要查询没有特定标签的帖子。没有多想,我就把=改成了!=

... INNER JOIN tag_post ON post.id = tag_post.postid 
WHERE tag_post.tagid != certain_tagid`

轰隆隆!逻辑错误!

我确实想出了这个 - 只是在这里写逻辑:

... INNER JOIN tag_post ON post.id = tag_post.postid 
WHERE tag_post.postid NOT IN 
(SELECT postid from tag_post where tagid = certain_tagid)

我知道这会起作用,但由于我的成长方式,每当我使用子查询编写查询时,我都会感到内疚(无论是否合理)。

建议一个更好的方法来做到这一点?

【问题讨论】:

【参考方案1】:

您可以将其视为“查找帖子中所有标签中不匹配的行(针对特定标签)”

这是 LEFT JOIN 的教科书用例。

LEFT JOIN tag_post ON post.id = tag_post.postid AND tag_post.tagid = required_tagid
WHERE tag_post.tag_id IS NULL

请注意,您必须在连接的 ON 子句中包含标记 id。

有关连接类型的参考,请参见此处:http://www.codinghorror.com/blog/2007/10/a-visual-explanation-of-sql-joins.html

【讨论】:

谢谢 - 您的回复也明确了 ON 和 WHERE 子句之间的区别 - 可以在 INNER 连接中互换使用。链接也很棒!【参考方案2】:

除了 Gavin Towey 的好答案,您还可以使用 not exists 子查询:

where   not exists
        (
        select  *
        from    tag_post
        where   post.id = tag_post.postid
                and tag_post.tagid = required_tagid
        )

数据库通常以相同的方式执行这两种变体。我个人认为not exists 方法更易于阅读。

【讨论】:

@Quassnoi 写了一篇关于 mysql 中反连接模式的 blog article 并得出结论,EXISTS 的效率比替代方案低 30%。【参考方案3】:

    当我想获取包含 tagIdA 和 tagIdB 的帖子时,我必须使用两个连接(我最终同意了)。

    还有其他方法。

    可以通过对tag_post 仅针对这些标签进行分组过滤,然后按帖子分组,然后删除包含少于预期标签的任何组,来获取所有标记为tagid 123 和456 的帖子中的所有id ;然后可以使用结果过滤posts 表:

    SELECT * FROM posts WHERE id IN (
      SELECT   postid
      FROM     tag_post
      WHERE    tagid IN (123,456)
      GROUP BY postid
      HAVING   COUNT(*) = 2
    )
    

    如果一个帖子可以多次使用相同的tagid 标记,您需要将COUNT(*) 替换为性能较差的COUNT(DISTINCT tagid)

    现在,我需要查询没有特定标签的帖子。

    这称为反连接。最简单的方法是按照您的建议将上面查询中的IN 替换为NOT IN。我不会为此感到太内疚。另一种方法是使用外连接,如@GavinTowey's answer 中所建议的那样。

【讨论】:

不错。但是子查询的罪恶感开始了。但是我认为在一定数量的 AND 之后,这将比多个连接更有效。 @merlinbeard:您也可以与从子查询中具体化的表形成联接(分别用于上述情况 1 和 2 的内部联接和外部联接);它会产生等效的执行计划,因此纯粹是表面上的改变。我发现这种方法更具可读性。正如您所说,当搜索多个标签时,无子查询方法需要多次扫描(然后加入)tag_post,这几乎肯定效率较低。 @eggyal -- 有一种更好的方法可以让您使用相同的连接:SELECT * FROM posts JOIN tag_post ON ... WHERE tagid IN (123,456) GROUP BY postid HAVING SUM (tagid=123) AND SUM(tagid=456) @GavinTowey:哦,我喜欢这样! :)

以上是关于选择没有特定标签的帖子的主要内容,如果未能解决你的问题,请参考以下文章

使用连接的帖子的 MySQL 全选未选择(添加)标签

使用 JSoup 选择以特定模式开头的标签

sqlalchemy,使用反向包含(不在)子列值列表中进行选择

如何在 XSLT 中的特定树层次结构中选择标签?

在 jQuery 中选择一个特定的类

如何在文本区域标签中的特定光标位置插入选择标签下拉值作为文本片段?