选择没有特定标签的帖子
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:哦,我喜欢这样! :)以上是关于选择没有特定标签的帖子的主要内容,如果未能解决你的问题,请参考以下文章