快速找到与PostgreSQL相似的字符串

时间:2012-06-28 17:10:05

标签: sql postgresql text similarity postgresql-performance

我需要在表格中创建类似字符串的排名。

我有下表

create table names (
name character varying(255)
);

目前,我正在使用提供similarity功能的 pg_trgm 模块,但我遇到了效率问题。我创建了一个像Postgres manual suggests

这样的索引
CREATE INDEX trgm_idx ON names USING gist (name gist_trgm_ops);

我正在执行以下查询:

select (similarity(n1.name, n2.name)) as sim, n1.name, n2.name
from names n1, names n2
where n1.name != n2.name and similarity(n1.name, n2.name) > .8
order by sim desc;

查询有效,但是当你有数百个名字时,它确实很慢。此外,也许我忘了一点SQL,但我不明白为什么我不能使用条件and sim > .8而不会得到“列sim不存在”错误。

我希望有任何提示可以加快查询速度。

1 个答案:

答案 0 :(得分:62)

Postgres 9.6中,配置参数pg_trgm.similarity_threshold替换了函数set_limit()show_limit()。这些函数已弃用但仍然有效。

自Postgres 9.1以来,GIN和GiST指数的表现也以多种方式得到改善。


使用set_limit()% operator代替。两者都由pg_trgm模块提供。

你拥有它的方式,必须计算每个元素和表的每个其他元素之间的相似性(几乎是交叉连接)。如果您的表有1000行,那已经是1,000,000(!)相似度计算,之前那些可以根据条件进行检查并排序。请尝试改为:

SET pg_trgm.similarity_threshold = 0.8; -- Postgres 9.6 or later
-- SELECT set_limit(0.8);               -- for older versions

SELECT similarity(n1.name, n2.name) AS sim, n1.name, n2.name
FROM   names n1
JOIN   names n2 ON n1.name <> n2.name
               AND n1.name % n2.name
ORDER  BY sim DESC;

数量级更快,但仍然很慢。

您可能希望通过在交叉加入之前添加前提条件(例如匹配第一个字母)来限制可能的对数(并使用匹配的功能索引支持)。 交叉连接的性能会因 O(N²)而恶化。


关于你的附属问题:

WHERE ... sim > 0.8

不起作用,因为无法引用WHEREHAVING子句中的输出列。这是根据(有点混乱,授予)SQL标准 - 由某些其他RDBMS相当松散地处理。

另一方面:

ORDER BY sim DESC

工作,因为<{1}}和GROUP BY可以使用输出列 。详细说明:

测试用例

我在旧的测试服务器上进行了快速测试以验证我的声明 PostgreSQL 9.1.4。使用ORDER BY(最好的5个)的时间。

EXPLAIN ANALYZE

使用GIN指数的第一轮测试:

CREATE TEMP table t AS 
SELECT some_col AS name FROM some_table LIMIT 1000;  -- real life test strings

GIST指数的第二轮测试:

CREATE INDEX t_gin ON t USING gin(name gin_trgm_ops);  -- round1: with GIN index

新查询:

DROP INDEX t_gin;
CREATE INDEX t_gist ON t USING gist(name gist_trgm_ops);

使用的GIN索引,64次点击:总运行时间:484.022 ms
使用的GIST索引,64次点击:总运行时间: 248.772 ms

旧查询:

SELECT set_limit(0.8);

SELECT similarity(n1.name, n2.name) AS sim, n1.name, n2.name
FROM   t n1
JOIN   t n2 ON n1.name <> n2.name
           AND n1.name % n2.name
ORDER  BY sim DESC;

GIN索引使用,64次点击:总运行时间:6345.833 ms
使用GIST索引,64次点击:总运行时间: 6335.975 ms

否则结果相同。建议很好。这是只有1000行

GIN或GiST?

GIN通常提供卓越的读取性能:

But not in this particular case:

  

这可以通过GiST索引非常有效地实现,但不能通过   GIN索引。