PostgreSQL 中文参考文档
🌐 英文原版 官方 ↗

F.16. fuzzystrmatch — 判断字符串相似度与距离 #

F.16.1. Soundex
F.16.2. Daitch-Mokotoff Soundex
F.16.3. Levenshtein
F.16.4. Metaphone
F.16.5. Double Metaphone

fuzzystrmatch 模块提供多个函数,用于判断字符串之间的相似度与距离。

Caution

目前,soundex、metaphone、dmetaphone 与 dmetaphone_alt 函数对多字节编码(例如 UTF-8)效果不佳。对这类数据请使用 daitch_mokotoff 或 levenshtein。

该模块被视为“可信”模块,也就是说,拥有当前数据库 CREATE 权限的非超级用户也可以安装它。

F.16.1. Soundex #

Soundex 系统通过将发音相似的姓名转换为相同的代码来匹配它们。它最初被美国人口普查用于 1880 年、1900 年与 1910 年的人口调查。请注意,Soundex 对非英语姓名用处不大。

fuzzystrmatch 模块提供两个用于处理 Soundex 代码的函数:

soundex(text) returns text
difference(text, text) returns int

soundex 函数将字符串转换为其 Soundex 代码。difference 函数将两个字符串转换为各自的 Soundex 代码,然后报告匹配的代码位置数。由于 Soundex 代码有 4 个字符,结果范围从 0 到 4,0 表示不匹配,4 表示精确匹配。(因此该函数其实名不副实 — similarity 会是更好的名字。)

以下是一些用法示例:

SELECT soundex('hello world!');

SELECT soundex('Anne'), soundex('Ann'), difference('Anne', 'Ann');
SELECT soundex('Anne'), soundex('Andrew'), difference('Anne', 'Andrew');
SELECT soundex('Anne'), soundex('Margaret'), difference('Anne', 'Margaret');

CREATE TABLE s (nm text);

INSERT INTO s VALUES ('john');
INSERT INTO s VALUES ('joan');
INSERT INTO s VALUES ('wobbly');
INSERT INTO s VALUES ('jack');

SELECT * FROM s WHERE soundex(nm) = soundex('john');

SELECT * FROM s WHERE difference(s.nm, 'john') > 2;

F.16.2. Daitch-Mokotoff Soundex #

与原始 Soundex 系统一样,Daitch-Mokotoff Soundex 通过将发音相似的姓名转换为相同的代码来匹配它们。不过,Daitch-Mokotoff Soundex 对非英语姓名的用处明显大于原始系统。它相对原始系统的主要改进包括:

  • 代码基于前 6 个有意义的字母,而不是 4 个。

  • 一个字母或字母组合映射到 10 种可能的代码,而不是 7 种。

  • 当两个连续的字母发同一个音时,它们被编码为单个数字。

  • 当一个字母或字母组合可能有不同读音时,会发出多个代码以覆盖所有可能性。

该函数为其输入生成 Daitch-Mokotoff soundex 代码:

daitch_mokotoff(source text) returns text[]

结果可能包含一个或多个代码,取决于存在多少种合理的发音,因此它表示为数组。

由于 Daitch-Mokotoff soundex 代码只由 6 位数字组成,source 最好是单个单词或姓名。

以下是一些示例:

SELECT daitch_mokotoff('George');
 daitch_mokotoff
-----------------
 {595000}

SELECT daitch_mokotoff('John');
 daitch_mokotoff
-----------------
 {160000,460000}

SELECT daitch_mokotoff('Bierschbach');
                      daitch_mokotoff
-----------------------------------------------------------
 {794575,794574,794750,794740,745750,745740,747500,747400}

SELECT daitch_mokotoff('Schwartzenegger');
 daitch_mokotoff
-----------------
 {479465}

对于单个姓名的匹配,返回的文本数组可以直接用 && 操作符匹配:任何重叠都可以视为匹配。为提高效率可以使用 GIN 索引,参见 Section 65.4 与以下示例:

CREATE TABLE s (nm text);
CREATE INDEX ix_s_dm ON s USING gin (daitch_mokotoff(nm)) WITH (fastupdate = off);

INSERT INTO s (nm) VALUES
  ('Schwartzenegger'),
  ('John'),
  ('James'),
  ('Steinman'),
  ('Steinmetz');

SELECT * FROM s WHERE daitch_mokotoff(nm) && daitch_mokotoff('Swartzenegger');
SELECT * FROM s WHERE daitch_mokotoff(nm) && daitch_mokotoff('Jane');
SELECT * FROM s WHERE daitch_mokotoff(nm) && daitch_mokotoff('Jens');

对于以任意顺序匹配任意数量的姓名,可以使用全文搜索功能。参见 Chapter 12 与以下示例:

CREATE FUNCTION soundex_tsvector(v_name text) RETURNS tsvector
BEGIN ATOMIC
  SELECT to_tsvector('simple',
                     string_agg(array_to_string(daitch_mokotoff(n), ' '), ' '))
  FROM regexp_split_to_table(v_name, '\s+') AS n;
END;

CREATE FUNCTION soundex_tsquery(v_name text) RETURNS tsquery
BEGIN ATOMIC
  SELECT string_agg('(' || array_to_string(daitch_mokotoff(n), '|') || ')', '&')::tsquery
  FROM regexp_split_to_table(v_name, '\s+') AS n;
END;

CREATE TABLE s (nm text);
CREATE INDEX ix_s_txt ON s USING gin (soundex_tsvector(nm)) WITH (fastupdate = off);

INSERT INTO s (nm) VALUES
  ('John Doe'),
  ('Jane Roe'),
  ('Public John Q.'),
  ('George Best'),
  ('John Yamson');

SELECT * FROM s WHERE soundex_tsvector(nm) @@ soundex_tsquery('john');
SELECT * FROM s WHERE soundex_tsvector(nm) @@ soundex_tsquery('jane doe');
SELECT * FROM s WHERE soundex_tsvector(nm) @@ soundex_tsquery('john public');
SELECT * FROM s WHERE soundex_tsvector(nm) @@ soundex_tsquery('besst, giorgio');
SELECT * FROM s WHERE soundex_tsvector(nm) @@ soundex_tsquery('Jameson John');

如果希望避免在索引复查期间重新计算 soundex 代码,可以使用单独列上的索引代替表达式上的索引。为此可以使用存储生成列;参见 Section 5.4。

F.16.3. Levenshtein #

该函数计算两个字符串之间的 Levenshtein 距离:

levenshtein(source text, target text, ins_cost int, del_cost int, sub_cost int) returns int
levenshtein(source text, target text) returns int
levenshtein_less_equal(source text, target text, ins_cost int, del_cost int, sub_cost int, max_d int) returns int
levenshtein_less_equal(source text, target text, max_d int) returns int

source 与 target 可以是任意非空字符串,最长 255 个字符。代价参数分别指定一次字符插入、删除或替换的代价。可以像该函数的第二个版本那样省略代价参数;此时它们都默认为 1。

levenshtein_less_equal 是 Levenshtein 函数的加速版本,适用于只关心较小距离的场景。如果实际距离小于或等于 max_d,levenshtein_less_equal 返回正确的距离;否则它返回某个大于 max_d 的值。如果 max_d 为负,其行为与 levenshtein 相同。

示例:

test=# SELECT levenshtein('GUMBO', 'GAMBOL');
 levenshtein
-------------
           2
(1 row)

test=# SELECT levenshtein('GUMBO', 'GAMBOL', 2, 1, 1);
 levenshtein
-------------
           3
(1 row)

test=# SELECT levenshtein_less_equal('extensive', 'exhaustive', 2);
 levenshtein_less_equal
------------------------
                      3
(1 row)

test=# SELECT levenshtein_less_equal('extensive', 'exhaustive', 4);
 levenshtein_less_equal
------------------------
                      4
(1 row)

F.16.4. Metaphone #

Metaphone 与 Soundex 一样,基于为输入字符串构造代表性代码的思想。如果两个字符串具有相同的代码,就认为它们相似。

该函数计算输入字符串的 metaphone 代码:

metaphone(source text, max_output_length int) returns text

source 必须是最长 255 个字符的非空字符串。max_output_length 设置输出 metaphone 代码的最大长度;如果更长,输出将被截断到该长度。

示例:

test=# SELECT metaphone('GUMBO', 4);
 metaphone
-----------
 KM
(1 row)

F.16.5. Double Metaphone #

Double Metaphone 系统为给定输入字符串计算两个“发音类似”字符串 — 一个“主”代码与一个“备选”代码。在大多数情况下两者相同,但对于非英语姓名,尤其是在发音不同的场合,它们可能略有差异。这些函数计算主代码与备选代码:

dmetaphone(source text) returns text
dmetaphone_alt(source text) returns text

输入字符串没有长度限制。

示例:

test=# SELECT dmetaphone('gumbo');
 dmetaphone
------------
 KMP
(1 row)