我需要在表中创建类似字符串的排名。
我有下表
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不存在”错误。
我想要任何提示使查询更快。
在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 2)而恶化。
至于你的附属问题:
WHERE ... sim > 0.8
不起作用,因为你不能引用WHERE
或HAVING
子句中的输出列。这是根据(有点混乱,授予)SQL标准 - 由某些其他RDBMS相当松散地处理。
另一方面:
ORDER BY sim DESC
因为输出列可以在GROUP BY
和ORDER BY
中使用,所以可以正常工作。细节:
我在旧的测试服务器上运行了一个快速测试来验证我的声明。
PostgreSQL 9.1.4。用EXPLAIN ANALYZE
(最好的5个)的时间。
CREATE TEMP table t AS
SELECT some_col AS name FROM some_table LIMIT 1000; -- real life test strings
第一轮GIN指数测试:
CREATE INDEX t_gin ON t USING gin(name gin_trgm_ops); -- round1: with GIN index
GIST指数的第二轮测试:
DROP INDEX t_gin;
CREATE INDEX t_gist ON t USING gist(name gist_trgm_ops);
新查询:
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次点击:总运行时间:484.022 ms 使用的GIST索引,64次点击:总运行时间:248.772 ms
旧查询:
SELECT (similarity(n1.name, n2.name)) as sim, n1.name, n2.name
FROM t n1, t n2
WHERE n1.name != n2.name
AND similarity(n1.name, n2.name) > 0.8
ORDER BY sim DESC;
未使用GIN索引,64次点击:总运行时间:6345.833 ms 未使用GIST索引,64次点击:总运行时间:6335.975 ms
否则结果相同。建议很好。这只是1000行!
GIN通常提供卓越的读取性能:
But not in this particular case:
这可以通过GiST索引非常有效地实现,但不能通过GIN索引实现。