表:文章
+--------+------+------------+
| id | title| created |
+--------+------+------------+
| 201 | AAA | 1482561011 |
| 202 | BBB | 1482561099 |
| 203 | CCC | 1482562188 |
+--------+------+------------+
表:标签
+-----------+------+
| articleid | tagid|
+-----------+------+
| 201 | 11 |
| 201 | 12 |
| 202 | 11 |
| 202 | 13 |
| 202 | 14 |
+-----------+------+
现在,如果给出 3个标签ID ,那么选择每篇文章同时匹配3个标签ID 的最新10篇文章的最佳索引设计和查询是什么?
我知道有几种方法可以做到,但我关注性能,考虑到每个标签中可能有成千上万篇文章
答案 0 :(得分:1)
select distinct on (a.id) a.*
from articles a
join taggings t on t.articleid = a.id
group by a.id
having array_agg(t.tagid order by t.tagid) = array[11,13,14]
order by a.id, a.created
limit 10;
taggings (articleid, tagid)
上的索引对此有帮助。
请注意,上面的内容会查找完全这三个标记的文章。如果你想找到至少这三个标签(可能还有更多)的那些,你可以改变having子句来使用" contains"操作者:
select distinct on (a.id) a.*
from articles a
join taggings t on t.articleid = a.id
where t.tagid in (11,13,14)
group by a.id
having array_agg(t.tagid) @> array[11,13,14]
order by a.id, a.created
limit 10;
在这种情况下,order by
array_agg()
不是必需的
答案 1 :(得分:1)
如a_horse_with_no_name所述,此博客文章具有一些非常有趣的性能基准,用于查找与多个标签匹配的行:
http://www.databasesoup.com/2015/01/tag-all-things.html
将标签存储在主表的数组列中并创建GIN-index允许像这样选择行,而无需任何连接:
select id
from articles
where tags @> array[11,13,14]
order by created desc
limit 10;
可以像这样创建列和索引:
alter table articles add column tags text[] not null default '{}';
create index tags_index on articles using gin (tags);
根据博客报道,使用数组列的速度比加入标签表的速度快8到895倍。
答案 2 :(得分:0)
您需要在articles.created
上设置索引进行排序,并在taggings(articleid, tagid)
上使用另一个唯一索引进行查询:
CREATE INDEX ON articles(created);
CREATE UNIQUE INDEX ON taggings(articleid, tagid);
然后只使用三个taggings
表别名进行选择查询:
SELECT a.* FROM articles a, taggings t1, taggings t2, taggings t3
WHERE a.id=t1.articleid AND a.id=t2.articleid AND a.id=t3.articleid
AND t1.tagid=111 AND t2.tagid=222 AND t3.tagid=333
ORDER BY created DESC LIMIT 10;
答案 3 :(得分:0)
就我而言,我必须对每个“标签”应用一个复杂的条件,而@>
毫无用处。所以我找到了另一种方法:一个网格传感器数组:
ARRAY[
bool_or(true if tag1 is present),
bool_or(true if tag2 is present),
...
] = ARRAY[true, true, ...]
示例:
SELECT a.*
FROM articles a JOIN tags t ON(t.articleid = a.id)
GROUP BY a.id
HAVING
ARRAY[
bool_or(t.tagid == 11),
bool_or(t.tagid == 13),
bool_or(t.tagid == 14)
] == ARRAY[true, true, true]
它的性能很差,但对于许多很多关系却具有很大的灵活性。