Mysql 选择没有特定标记的帖子

Mysql 选择没有特定标记的帖子,mysql,sql,many-to-many,subquery,Mysql,Sql,Many To Many,Subquery,我有一个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的帖子时,我必须使用两个连接(我最终有点接受了) 现在,我需要查询

我有一个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})
我知道这会起作用,但由于我的成长方式,每当我用子查询编写查询时,我都会感到内疚(不管是否合理)

建议更好的方法吗?

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

这是关于左连接的教科书用例

LEFT JOIN tag_post ON post.id = tag_post.postid AND tag_post.tagid = {required_tagid}
WHERE tag_post.tag_id IS NULL
注意,您必须在join的ON子句中具有标记id

有关联接类型的参考,请参见此处:

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

这是关于左连接的教科书用例

LEFT JOIN tag_post ON post.id = tag_post.postid AND tag_post.tagid = {required_tagid}
WHERE tag_post.tag_id IS NULL
注意,您必须在join的ON子句中具有标记id


有关联接类型的参考,请参见此处:

除了Gavin Toway的好答案之外,您还可以使用
不存在的子查询:

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

数据库通常以相同的方式执行这两个变量。我个人认为
不存在
方法更容易阅读。

除了加文·托维的好答案外,你还可以使用
不存在
子查询:

where   not exists
        (
        select  *
        from    tag_post
        where   post.id = tag_post.postid
                and tag_post.tagid = {required_tagid}
        )
数据库通常以相同的方式执行这两个变量。我个人认为
不存在
方法更容易阅读

  • 当我想要获取包含tagIdA和tagIdB的帖子时,我必须使用两个连接(我最终有点接受了)

    还有其他方法

    通过仅对那些标签进行分组筛选,按标签分组,然后删除包含少于预期标签的任何组,可以获得所有同时标记了
    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(不同的tagid)

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

    这称为反连接。最简单的方法是按照您的建议,将上面查询中的
    替换为
    而不是
    。我不会为此感到太内疚。另一种方法是使用外部联接,如中所建议

  • 当我想要获取包含tagIdA和tagIdB的帖子时,我必须使用两个连接(我最终有点接受了)

    还有其他方法

    通过仅对那些标签进行分组筛选,按标签分组,然后删除包含少于预期标签的任何组,可以获得所有同时标记了
    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(不同的tagid)

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

    这称为反连接。最简单的方法是按照您的建议,将上面查询中的
    替换为
    而不是
    。我不会为此感到太内疚。另一种方法是使用外部联接,如中所建议


  • @Quassnoi在MySQL中写了一篇关于反连接模式的文章,得出结论认为
    存在
    的效率比备选方案低30%。@Quassnoi在MySQL中写了一篇关于反连接模式的文章,得出结论认为
    存在
    的效率比备选方案低30%。很好。但是子查询的负罪感开始出现了。然而,我认为经过一定数量的and之后,这将比多个联接更有效。@merlinbeard:您也可以使用从子查询中具体化的表来形成联接(分别用于上述情况1和2的内部联接和外部联接);它产生了一个等效的执行计划,因此纯粹是一个表面上的改变。我发现这种方法更具可读性。正如您所说,当您搜索多个标记时,无子查询方法需要多次扫描(然后连接)
    tag\u post
    ,这几乎肯定效率较低。@eggyal--有一种更好的方法可以使用相同的连接:选择*FROM posts join tag\u post ON。。。其中,tagid在(123456)组中,通过postid具有SUM(tagid=123)和SUM(tagid=456)@gavintoway:Ooooh,我喜欢这样!)美好的但是子查询的负罪感开始出现了。然而,我认为经过一定数量的and之后,这将比多个联接更有效。@merlinbeard:您也可以使用从子查询中具体化的表来形成联接(分别用于上述情况1和2的内部联接和外部联接);它产生了一个等效的执行计划,因此纯粹是一个表面上的改变。我发现这种方法更具可读性。正如您所说,当您搜索多个标记时,无子查询方法需要多次扫描(然后连接)
    tag\u post
    ,这几乎肯定效率较低。@eggyal--有一种更好的方法可以使用相同的连接:选择*FROM posts join tag\u post ON。。。其中,tagid在(123456)组中,通过postid具有SUM(tagid=123)和SUM(tagid=456)@gavintoway:Ooooh,我喜欢这样!)谢谢-您的回复还明确了ON和WHERE子句之间的区别-可以在CHAN中使用