我的SQL设置类似于以下内容:
制品
标签
...以及第二个记录两者之间关联的表,因为每篇文章可以有多个标签:
ARTICLE_TAG_ASSOCS
通过this question我设法构建了一个查询,该查询可以找到用多个标签中的至少一个标记的文章,例如。
SELECT articles.*
FROM articles
JOIN article_tag_assocs ata ON articles.id = ata.article_id
JOIN tags ON tags.id = ata.tag_id
WHERE tags.tag = 'budgie' OR tags.tag = 'parrot';
问题:如何更改以上内容以查找与所有标签匹配的文章,即“疯狂”和“#”和' parrot',而不只是一个?
明确地将逻辑修改为
WHERE tags.tag = 'budgie' && tags.tag = 'parrot';
...在逻辑上存在缺陷,因为MySQL正在单独考虑每个标签,一次一个,但希望你明白我的意思。
答案 0 :(得分:2)
有几种可行的方法。
一种方法是为每个标签执行单独的JOIN操作。例如:
SELECT articles.*
FROM articles
JOIN article_tag_assocs ata
ON ata.article_id = articles.id
JOIN tags ta
ON ta.id = ata.tag_id
AND ta.tag = 'budgie'
JOIN article_tag_assocs atb
ON atb.article_id = articles.id
JOIN tags tb
ON tb.id = atb.tag_id
AND tb.tag = 'parrot'
请注意,如果给定的文章多次与同一标记值相关联,则可以返回“重复”行。 (添加DISTINCT
关键字或GROUP BY
子句是消除重复项的方法。)
另一种方法是,如果我们保证给定文章没有重复的标记值,则使用内联视图来获取与这两个标记关联的article_id
列表,然后将JOIN设置为articles
表。例如:
SELECT a.*
FROM ( SELECT ata.article_id
FROM article_tag_assocs ata
JOIN tags t
ON t.id = ata.tag_id
WHERE t.tag IN ('budgie','parrot')
GROUP BY ata.article_id
HAVING COUNT(1) = 2
) s
JOIN articles a
ON a.id = s.article_id
请注意,HAVING子句中的文字“2”与tag
列上谓词中的值的数量相匹配。内联视图(别名为s
)返回article_id
的不同列表,我们可以将其加入articles
表。
如果您希望匹配至少三个 四个标记,则此方法非常有用。我们可以在内联视图查询中使用这样的行。
WHERE t.tag IN ('fee','fi','fo','fum')
HAVING COUNT(1) >= 3
然后,将返回与这四个标签中至少三个匹配的任何文章。
这些不是返回指定结果的唯一方法,还有其他几种方法。
正如罗兰的回答所指出的那样,你也可以这样做:
FROM articles a
WHERE a.id IN ( <select article id values related to tag 'parrot'> )
AND a.id IN ( <select article id values related to tag 'bungie'> )
您也可以使用带有相关子查询的EXISTS子句,但由于子查询的执行次数,这种方法通常不能与大型集合一起使用
FROM articles a
WHERE EXISTS ( SELECT 1
FROM article_tag_assocs s1
JOIN tags t1 ON t1.tag = 'bungie'
WHERE s1.article_id = a.id
)
AND EXISTS ( SELECT 1
FROM article_tag_assocs s2
JOIN tags t2 ON t2.tag = 'parrot'
WHERE s2.article_id = a.id
)
注意:在这种情况下,可以在每个子查询中重用相同的表别名,因为它不会导致歧义,但我仍然喜欢不同的别名,因为表别名显示在EXPLAIN输出中,并且不同的别名可以更容易地将EXPLAIN输出中的行与查询中的引用相匹配。)