为此使用unaccent module - 这与您链接的内容完全不同。
unaccent 是一个文本搜索字典,可以去除重音符号(变音符号
符号)来自词位。
每个数据库安装一次:
CREATE EXTENSION unaccent;
如果您收到如下错误:
ERROR: could not open extension control file
"/usr/share/postgresql/<version>/extension/unaccent.control": No such file or directory
按照相关答案中的说明在您的数据库服务器上安装 contrib 包:
除其他外,它还提供了您可以在示例中使用的函数unaccent()(似乎不需要LIKE)。
SELECT *
FROM users
WHERE unaccent(name) = unaccent('João');
索引
要对此类查询使用索引,请创建index on the expression。 但是,Postgres 只接受 IMMUTABLE 函数作为索引。如果函数可以为相同的输入返回不同的结果,则索引可能会静默中断。
unaccent() 仅 STABLE 不是 IMMUTABLE
很遗憾,unaccent() 只是 STABLE,而不是 IMMUTABLE。根据this thread on pgsql-bugs,这是由于三个原因:
- 这取决于字典的行为。
- 这本词典没有硬连线连接。
- 因此它还取决于当前的
search_path,它可以轻松更改。
网络上的Some tutorials 指示只需将函数波动性更改为IMMUTABLE。这种暴力破解方法在某些情况下可能会崩溃。
其他人建议simple IMMUTABLE wrapper function(就像我过去自己做的那样)。
关于是否使用明确声明使用的字典的variant with two parameters IMMUTABLE 一直存在争议。阅读here 或here。
另一种选择是这个模块带有IMMUTABLE unaccent() function by Musicbrainz,在 Github 上提供。自己没有测试过。我想我想出了一个更好的主意:
目前最佳
这种方法比其他浮动解决方案更有效,也更安全。
创建一个IMMUTABLE SQL 包装函数,执行带有硬连线模式限定函数和字典的双参数形式。
由于嵌套非不可变函数会禁用函数内联,因此它也基于声明为 IMMUTABLE 的 C 函数(假)的副本。它的唯一目的是在SQL函数包装器中使用。不能单独使用。
需要复杂性,因为无法在 C 函数的声明中硬连线字典。 (需要破解 C 代码本身。)SQL 包装函数执行此操作,并允许函数内联 和 表达式索引。
CREATE OR REPLACE FUNCTION public.immutable_unaccent(regdictionary, text)
RETURNS text LANGUAGE c IMMUTABLE PARALLEL SAFE STRICT AS
'$libdir/unaccent', 'unaccent_dict';
CREATE OR REPLACE FUNCTION public.f_unaccent(text)
RETURNS text LANGUAGE sql IMMUTABLE PARALLEL SAFE STRICT AS
$func$
SELECT public.immutable_unaccent(regdictionary 'public.unaccent', $1)
$func$;
从 Postgres 9.5 或更早版本的两个函数中删除 PARALLEL SAFE。
public 是您安装扩展的架构(public 是默认值)。
显式类型声明 (regdictionary) 可防御恶意用户使用函数的重载变体进行的假设攻击。
之前,我提倡基于 unaccent 模块附带的 STABLE 函数 unaccent() 的包装函数。那禁用了function inlining。这个版本的执行速度比我之前在这里的简单包装函数快十倍。
这已经比在函数中添加SET search_path = public, pg_temp 的第一个版本快两倍——直到我发现字典也可以是模式限定的。 Still (Postgres 12) not too obvious from documentation.
如果您缺乏创建 C 函数所需的权限,您将回到次优实现:模块提供的 STABLE unaccent() 函数的 IMMUTABLE 函数包装器:
CREATE OR REPLACE FUNCTION public.f_unaccent(text)
RETURNS text AS
$func$
SELECT public.unaccent('public.unaccent', $1) -- schema-qualify function and dictionary
$func$ LANGUAGE sql IMMUTABLE PARALLEL SAFE STRICT;
最后,表达式索引使查询快速:
CREATE INDEX users_unaccent_name_idx ON users(public.f_unaccent(name));
记住在对函数或字典进行任何更改后重新创建索引涉及此函数,例如不会重新创建索引的就地主要版本升级。最近的主要版本都更新了 unaccent 模块。
调整查询以匹配索引(因此查询计划器将使用它):
SELECT * FROM users
WHERE f_unaccent(name) = f_unaccent('João');
您不需要正确表达式中的函数。在那里,您还可以直接提供像'Joao' 这样的无重音字符串。
使用 expression index 时,更快的功能不会转化为更快的查询。这在预先计算的值上运行,并且已经非常快了。但是索引维护和查询不使用索引的好处。
Postgres 10.3 / 9.6.8 等版本加强了客户端程序的安全性。您需要按照在任何索引中使用时演示的模式限定函数和字典名称。见:
连字
在 Postgres 9.5 或更早版本中,必须手动扩展诸如 'Œ' 或 'ß' 之类的连字(如果需要),因为 unaccent() 总是替换为 单个 信:
SELECT unaccent('Œ Æ œ æ ß');
unaccent
----------
E A e a S
你会喜欢 Postgres 9.6 中的this update to unaccent:
扩展contrib/unaccent的标准unaccent.rules文件以处理所有
Unicode 已知的变音符号,并正确扩展连字(Thomas
门罗,伦纳德·贝内代蒂)
我的大胆强调。现在我们得到:
SELECT unaccent('Œ Æ œ æ ß');
unaccent
----------
OE AE oe ae ss
模式匹配
对于具有任意模式的 LIKE 或 ILIKE,将其与 PostgreSQL 9.1 或更高版本中的模块 pg_trgm 结合使用。创建三元组 GIN(通常更可取)或 GIST 表达式索引。 GIN 示例:
CREATE INDEX users_unaccent_name_trgm_idx ON users
USING gin (f_unaccent(name) gin_trgm_ops);
可用于以下查询:
SELECT * FROM users
WHERE f_unaccent(name) LIKE ('%' || f_unaccent('João') || '%');
GIN 和 GIST 索引的维护成本比普通 btree 高:
对于左锚定模式有更简单的解决方案。有关模式匹配和性能的更多信息:
pg_trgm 还提供有用的operators for "similarity" (%) and "distance" (<->)。
Trigram 索引还支持带有~ 等的简单正则表达式。和 不区分大小写 模式匹配 ILIKE: