我有一个包含 3,000,000 行或更多行的数据库表:
|ID|T4|T16|T64|TL
其中 ID =INTEGER (PK) 和 T4,T16,T64,TL 是文本字段。每一个的长度都比前一个大,即LENGTH(T4)
我想根据比较行的 TL 之间的 levenshtein 距离(fuzzystrmatch 扩展)将此表的一行与 SELF JOIN 与所有其他行进行比较,并获得与较小的 levenstein 距离相对应的最佳 k 结果。
SELECT n1.*,n2.*
FROM (SELECT * FROM dbtable WHERE ID =110) n1,
dbtable n2
ORDER BY levenshtein_less_equal(n1.TL,n2.TL,LENGTH(n1.TEXT)/4) LIMIT 100;
当然,这会非常慢,因为它必须进行 3,000,000 次 levenshtein 计算。
为了加速查询,我创建了人工“哈希”(字段 T4、T16、T64)。所以,我想首先检查字段 T4 之间的 levenshtein 距离。如果 T4 之间的距离在一定范围内,那么我会检查 T16 之间的距离,以此类推。这样,在每一步中,我都需要计算比天真的方法更少的 levenshtein 距离。
嵌套查询是这样的:
SELECT n5.*,levenshtein_less_equal(n5.n1_tl,n5.n2_tl,LENGTH(n5.n1_tl)/4)
FROM
(SELECT n4.*
FROM
(SELECT n3.*
FROM
(SELECT
n1.id AS n1_id,
n1.t4 AS n1_t4,
n1.t16 AS n1_t16,
n1.t64 AS n1_t64,
n1.tl AS n1_tl,
n2.id AS n2_id,
n2.t4 AS n2_t4,
n2.t16 AS n2_t16,
n2.t64 AS n2_t64,
n2.tl AS n2_tl
FROM (SELECT id,t4,t16,t64,tl FROM dbTable WHERE id=110) AS n1,dbTable AS n2
WHERE
n1.id<>n2.id
AND levenshtein_less_equal(n1.t4,n2.t4,LENGTH(n1.t4)) <= LENGTH(n1.t4)/2
) n3
WHERE levenshtein_less_equal(n3.n1_t16,n3.n2_t16,LENGTH(n3.n1_t16)/2) < LENGTH(n3.n1_t16)/2
) AS n4
WHERE levenshtein_less_equal(n4.n1_t64,n4.n2_t64,LENGTH(n4.n1_t64)/2) < LENGTH(n4.n1_t64)/2)
AS n5
ORDER BY levenshtein_less_equal(n5.n1_tl,n5.n2_tl,LENGTH(n5.n1_tl)/4)
LIMIT 100;
该查询比原来的天真查询要快得多,因为它只需要访问 112851 行 TL,但是:
解释计划:
Limit (cost=872281.90..872282.15 rows=100 width=1168)
-> Sort (cost=872281.90..872564.03 rows=112851 width=1168)
Sort Key: (levenshtein_less_equal(dbTable.tl, n2.tl, (length(dbTable.tl) / 4)))
-> Nested Loop (cost=0.00..867968.81 rows=112851 width=1168)
Join Filter: ((dbTable.id <> n2.id) AND (levenshtein_less_equal(dbTable.t4, n2.t4, length(dbTable.t4)) <= (length(dbTable.t4) / 2)) AND (levenshtein_less_equal(dbTable.t16, n2.t16, (length(dbTable.t16) / 2)) < (length(dbTable.t16) / 2)) AND (levenshtein_less_equal(dbTable.t64, n2.t64, (length(dbTable.t64) / 2)) < (length(dbTable.t64) / 2)))
-> Index Scan using dbTable_pkey on dbTable (cost=0.00..8.60 rows=1 width=584)
Index Cond: (id = 110)
-> Seq Scan on dbTable n2 (cost=0.00..699529.82 rows=3046982 width=584)
如您所见,问题在于查询优化器连接/折叠 T4、T16、T64 之间的 levenshtein 距离计算,如果不折叠它可能会更快,因为需要为 T64 制作更少的 levenshtein 距离。我使用了 SET from_collapse_limit=1; 以避免在同一会话中崩溃但没有任何改变。有没有办法在 Postgres 查询中强制执行这种分层功能?请记住,没有一个 T4...TL 被索引,因为我认为没有索引可以加速 levenshtein 距离。有什么建议可以进一步提高性能?
我建议使用附加模块pg_trgm提供的三元组索引。将其与字符串的长度相结合以获得有效的预选。
删除列
T4
,T16
andT64
(在单个语句中更快),然后运行VACUUM FULL
orCLUSTER
。安装
pg_trgm
. 详情在这里:tl
在和上创建一个 GiST 索引length(tl)
。有一个细节需要注意。关于 dba.SE 的相关答案涉及
pg_trgm
: