PostgreSQL,查找字符串因n个字符而异

时间:2013-08-21 08:22:35

标签: sql postgresql indexing pattern-matching

假设我有一个这样的表

id  data
1   0001
2   1000
3   2010
4   0120
5   0020
6   0002

sql fiddle demo

id是主键,data是固定长度字符串,其中字符可以是0,1,2。

有没有办法建立一个索引,所以我可以快速找到与给定字符串中的n个字符不同的字符串?比如字符串0001n = 1我希望获得第6行。

感谢。

1 个答案:

答案 0 :(得分:2)

levenshtein()功能,由附加模块fuzzystrmatch提供。它完全符合您的要求:

SELECT *
FROM   a
WHERE  levenshtein(data, '1110') = 1;

SQL Fiddle.

但它不是很快。大表缓慢,因为它不能使用索引。

您可能会获得附加模块pg_trgm提供的相似性距离运算符。这些可以使用链接手册页中详述的三元组索引。我没有到达任何地方,模块使用了不同的“相似性”定义。

一般来说问题似乎适合KNN ("k nearest neighbours") search pattern

如果您的案例与问题中的示例一样简单,您可以将LIKE与trigram GIN索引结合使用,这对于大表来说应该相当快:

SELECT *
FROM   a
WHERE  data <> '1110'
AND   (data LIKE '_110' OR
       data LIKE '1_10' OR
       data LIKE '11_0' OR
       data LIKE '111_');

显然,这种技术很快变得不可行,因为字符串越长且差异超过1

然而,由于字符串太短,任何查询都会匹配基表的相当大的百分比。因此,指数支持几乎不会给你任何东西。大多数情况下,Postgres按顺序扫描会更快。

我测试了10k和100k行,有和没有trigram GIN索引。由于约19%符合给定测试用例的标准,因此顺序扫描更快,levenshtein()仍然获胜。对于匹配少于约5%的行(依赖)的更具选择性的查询,使用索引的查询(更快)更快。