我有一个帖子/标签数据库,有通常的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})
我知道这会有效,但是由于我的态度,每当我用子查询编写查询时,我都会感到内疚(无论是否合理)。
建议更好的方法吗?
答案 0 :(得分:2)
您可以将其视为“在帖子中查找标签中没有匹配项的所有行(针对特定标签)”
这是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
答案 1 :(得分:1)
除了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
方法更容易理解。
答案 2 :(得分:1)
当我想要获取具有tagIdA和tagIdB的帖子时,我必须使用两个连接(我最终会达成协议)。
还有其他方法。
通过仅为这些标记分组过滤id
,按帖子分组然后删除任何组,可以获取所有tagid
123和456标记的帖子tag_post
包含的标签少于预期;然后可以使用结果来过滤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中建议的外连接。