按照您的方式,必须计算表中每个元素与每个其他元素之间的相似性(几乎是交叉连接)。如果您的表有 1000 行,那已经是 1,000,000 (!) 个相似性计算,之前这些可以根据条件检查并排序。规模非常大。
请改用SET pg_trgm.similarity_threshold 和% operator。两者都由pg_trgm 模块提供。这样,三元组 GiST 索引就可以发挥很大的作用。
配置参数pg_trgm.similarity_threshold 替换了 Postgres 9.6 中的函数set_limit() and show_limit()。不推荐使用的函数仍然有效(从 Postgres 13 开始)。此外,自 Postgres 9.1 以来,GIN 和 GiST 索引的性能在许多方面都有所提高。
试试吧:
SET pg_trgm.similarity_threshold = 0.8; -- Postgres 9.6 or later
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;
快几个数量级,但仍然很慢。
pg_trgm.similarity_threshold 是一个"customized" option,可以像任何其他选项一样处理。见:
您可能希望通过添加前置条件(例如匹配首字母)在交叉连接(并通过匹配的功能索引支持)来限制可能对的数量。 交叉连接的性能会随着 O(N²) 而下降。
这不起作用,因为您不能在WHERE 或HAVING 子句中引用输出列:
WHERE ... sim > 0.8
这是根据 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 毫秒
使用 GIST 索引,64 次点击:总运行时间:248.772 毫秒
旧查询:
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 毫秒
GIST 索引未使用,64 次点击:总运行时间:6335.975 毫秒
否则结果相同。建议很好。这仅适用于1000 行!
GIN 还是 GiST?
GIN 通常提供卓越的读取性能:
But not in this particular case!
这可以通过 GiST 索引非常有效地实现,但不能通过
GIN 索引。