3

我有一个引用一堆文章的表,该表包含这些文章的标签。像这样:

tag text
article_id bigint

我想选择所有带有一组标签的 article_ids,比如 tag1、tag2、tag3,但文章也可以附加 tag4、tag5。

我知道这会起作用:

SELECT article_id 
FROM tag WHERE tag='tag1' 
INTERSECT 
SELECT article_id 
FROM tag 
WHERE tag='tag2' 
INTERSECT 
SELECT article_id 
FROM tag 
WHERE tag='tag3'

这也将是:

SELECT article_id 
FROM tag 
WHERE tag IN ('tag1','tag2','tag3') 
GROUP BY article_id 
HAVING count(*) = 3

但我不确定这是最有效的方法。我也一直在玩下面的东西,但目前无法让它工作。

SELECT array_agg(tag) as arr,
       article_id 
FROM tag 
GROUP BY article_id 
HAVING arr = {tag1,tag2,tag3}

这似乎是其他人会遇到的常见问题,我想知道在这种情况下,INTERSECT 是否是最有效的查询。它适用于 PostgreSQL。

4

3 回答 3

1

我假设您想要更多article的列,而不仅仅是article_id. 不过,不会改变查询样式。

您正在处理的内容称为关系划分。在这个相关问题下可以看到有很多方法可以给这只猫剥皮- 包括关于索引和性能测试的建议。

我个人最喜欢的(而且很可能是最快的)是:

SELECT a.*
FROM   article a
JOIN   tag x USING (article_id)
JOIN   tag y USING (article_id)
JOIN   tag z USING (article_id)
WHERE  x.tag = 'tag1'
AND    y.tag = 'tag1'
AND    z.tag = 'tag3';

或者:

SELECT a.*
FROM   article a
WHERE  EXISTS (
   SELECT *
   FROM   tag x
   JOIN   tag y USING (article_id)
   JOIN   tag z USING (article_id)
   WHERE  x.article_id = a.article_id
   AND    x.tag = 'tag1'
   AND    y.tag = 'tag2'
   AND    z.tag = 'tag3'
   );

@David 已经解释了为什么您的第三个版本无法运行。但是无论如何你都不应该使用它,因为它不能使用索引,并且会比这里的任何其他方法慢一个数量级。

于 2012-03-12T05:39:59.667 回答
0

我个人喜欢第二种选择。但是您应该使用 PostgreSQL 的工具以及一些测试查询,看看哪个是最有效的。

第三个不按书面说明工作的原因是因为您需要为 array_agg() 指定排序顺序: 如何使 array_agg() 像 mySQL 中的 group_concat() 一样工作

于 2012-03-11T23:26:24.973 回答
0

在您的最后一个变体中,您对数组相等性不感兴趣。您对数组包含感兴趣,HAVING arr包含所有三个标签。

并且 array1 包含 array2array1 @> array2在 PostgreSQL 中。

我仍然希望 Erwin 的答案能够通过索引赢得性能方面的胜利。

于 2012-03-13T06:51:10.380 回答