排除具有多对多关系的行
Posted
技术标签:
【中文标题】排除具有多对多关系的行【英文标题】:Exclusion of rows with many-to-many relationships 【发布时间】:2011-10-10 13:52:22 【问题描述】:我有三个表:posts、tags 和 posts_has_tags(它们促进了帖子和标签之间的多对多关系)。一篇文章可以有任意数量的标签。
“帖子”表包含以下列: 发帖 文字
“标签”表有这些: 身份标签 名字
至于“posts_has_tags”表: post_idposts tags_idtags
我不能做的是提出一个查询来选择所有帖子,除了那些在分配给它们的“名称”列中具有特定值的标签(或标签)的帖子。似乎它应该包含“不存在”,但我无法完全理解它。
提前感谢您的帮助。
编辑:
另外,是否可以同时将结果集限制为某些标签?例如:
要排除的标签:a、b 包含标签:c
带有标签“a”、“f”的帖子不会进入结果集(因为如果包含标签,则没有)。 带有标签“a”、“b”、“c”的帖子也不会进入结果集(因为它的“a”和“b”是被排除的标签)。 带有标签'c'的帖子,'f'确实会进入结果集,因为'c'是包含的标签。
最终编辑 我终于找到了一个似乎有效且性能相当不错的解决方案:http://www.mysqldiary.com/a-many-to-many-relationship-table-solving-the-exclude-relation-problem/
【问题讨论】:
不清楚(已编辑部分),请您解释一下更好的方式。(重定向?)。 【参考方案1】:您可以使用反连接。
SELECT p.*
FROM posts p
LEFT JOIN post_has_tags pt ON (pt.post_id = p.id)
LEFT JOIN tags t ON (t.id = pt.tag_id AND t.name IN ('test','test1','test2'))
WHERE t.id IS NULL
GROUP BY p.id
如果你想强制包含其他标签,你可以做另一个连接。
SELECT p.*
FROM posts p
LEFT JOIN post_has_tags pt ON (pt.post_id = p.id)
LEFT JOIN tags t ON (t.id = pt.tag_id AND t.name IN ('a','b'))
INNER JOIN tags t2 ON (t2.id <> t.id AND t2.id = pt.tag_id AND t2.name IN ('c'))
WHERE t.id IS NULL
GROUP BY p.id
这将优先考虑排除而不是包含。 如果您想优先考虑包含,请将内部联接替换为:
INNER JOIN tags t2 ON (t2.id = pt.tag_id AND t2.name IN ('c'))
【讨论】:
你不需要GROUP BY p.id
吗?
@ypercube,是的,您需要SELECT DISTINCT
或更快的替代方案GROUP BY p.id
谢谢!您能否也看看我所做的编辑中描述的问题?
看来我选择这个答案太快了。似乎第一个将排除优先于包含的变体不起作用。无论我在“IN...”语句中放置什么标签,结果集始终为空。如果您能帮助我解决这个困难,我将不胜感激,因为我还不能真正完全理解您的代码。
第一个变体都不起作用,没有内部连接。它根本不会删除任何帖子,无论分配给它什么标签。【参考方案2】:
SELECT p.*
FROM posts AS p
WHERE NOT EXISTS
( SELECT *
FROM posts_has_tags AS pt
JOIN tags AS t
ON pt.tags_idtags = t.idtags
WHERE pt.posts_idposts = p.idposts
AND t.name = @CertainForbiddenTagName
)
如果您有很多标签名称要禁止,请改用这个:
AND t.name IN (List of ForbiddenTagNames)
对于您更新的第二个问题,只需添加类似的EXISTS
:
AND EXISTS
( SELECT *
...
)
【讨论】:
谢谢!您能否也看看我所做的编辑中描述的问题? 该查询是否可以优先考虑包含而不是排除?【参考方案3】:select * from posts where idposts in
(select posts_has_tags.posts_idposts from posts_has_tags
join tags on tags.idtags = posts_has_tags.tags_idtags
where tags.name not in ('value1','value2',...))
【讨论】:
谢谢!您能否也看看我所做的编辑中描述的问题?【参考方案4】:我终于找到了一个似乎有效且性能相当好的解决方案:http://www.mysqldiary.com/a-many-to-many-relationship-table-solving-the-exclude-relation-problem/
【讨论】:
上面链接中提到的解决方案是针对单表的。你能告诉我它是如何解决你的问题的,因为你有三张桌子。我有类似的问题,您在编辑之前已经在上面提到过以上是关于排除具有多对多关系的行的主要内容,如果未能解决你的问题,请参考以下文章