PostgreSQL 支持“重音不敏感”排序规则吗?

在 Microsoft SQL Server 中,可以指定一个“重音不敏感”的排序规则(对于数据库、表或列) ,这意味着可以进行如下查询

SELECT * FROM users WHERE name LIKE 'João'

查找具有 Joao名称的行。

我知道在 PostgreSQL 中可以使用 不重音字符串贡献函数从字符串中去除重音,但是我想知道 PostgreSQL 是否支持这些“重音不敏感”的排序,以便使用上面的 SELECT

69409 次浏览

我确信 PostgreSQL 依赖于底层的操作系统进行排序。它支持 创建新的排序自定义排序。不过我不确定这对你来说有多重要。(可能会很多)

Postgres 12或更高版本的更新

Postgres 12添加了 不确定性 < a href = “ https://en.wikipedia.org/wiki/International _ Component _ for _ Unicode”rel = “ nofollow norefrer”> ICU 排序,支持不区分大小写和不区分重音的分组和排序

只有在构建 PostgreSQL 时配置了对 ICU 的支持,才能使用 ICU 区域设置。

如果是这样,这对你有用:

CREATE COLLATION ignore_accent (provider = icu, locale = 'und-u-ks-level1-kc-true', deterministic = false);


CREATE INDEX users_name_ignore_accent_idx ON users(name COLLATE ignore_accent);


SELECT * FROM users WHERE name = 'João' COLLATE ignore_accent;

小提琴

详情请阅读手册。 这个 作者: Laurenz Albe可能有助于理解。

但是 ICU 排序也有 缺点.手册:

[ ... ]它们也有一些缺点。首先,它们的使用导致 特别要注意的是,B 树不能使用 使用不确定性排序规则的索引进行重复数据删除。 此外,某些操作是不可能与不确定性 排序规则,例如模式匹配操作 只有在特别需要的情况下才能使用。

我的“遗产”解决方案可能仍然更好:

所有的版本

使用 非重音模块-这是完全不同的,你正在链接到。

是一个文本搜索字典,删除重音符号(变音符号 来自词位的符号。

每个数据库安装一次:

CREATE EXTENSION unaccent;

如果出现以下错误:

ERROR: could not open extension control file
"/usr/share/postgresql/<version>/extension/unaccent.control": No such file or directory

按照下面相关答案中的指示,在数据库服务器上安装贡献包:

除了其他功能之外,它还提供了可以在示例中使用的函数 unaccent()(其中似乎不需要 LIKE)。

SELECT *
FROM   users
WHERE  unaccent(name) = unaccent('João');

索引

若要为此类查询使用索引,请创建一个 表达式的索引。Postgres 只接受 IMMUTABLE函数作为索引。如果一个函数可以为相同的输入返回不同的结果,那么索引可能会无声地中断。

只有 unaccent()不是 IMMUTABLE

不幸的是,unaccent()只是 STABLE,而不是 IMMUTABLE。根据 Pgsql-bug 上的这个线程,这是由于 的原因:

  1. 这取决于字典的行为。
  2. 这本字典没有硬接线连接。
  3. 因此,它也取决于当前的 search_path,这可以很容易地改变。

一些教程 在网上指示只改变函数的波动性到 IMMUTABLE。这种暴力方法在一定条件下可以破坏。

其他人建议使用 简单的 ABc0包装函式(就像我过去自己做的那样)。

有一个正在进行的争论是否使 有两个参数的变量 IMMUTABLE声明使用的字典显式。阅读 给你给你

另一个替代方案是在 Github 上提供的带有 不可变 unaccent()函数的模块。我自己还没测试过。我想我已经想出了一个 更好的主意:

暂时最好

这种方法是 比其他流动的解决方案更有效,更安全
创建一个使用硬连接的模式限定函数和字典执行双参数表单的 IMMUTABLE SQL 包装函式。

由于嵌套一个非不可变的函数会禁用函数内联,所以它基于 C 函数的一个副本,(假)也声明了 IMMUTABLE。它的 只有用途是在 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 14或更高版本中,SQL 标准函数稍微便宜一些,但是:

CREATE OR REPLACE FUNCTION public.f_unaccent(text)
RETURNS text
LANGUAGE sql IMMUTABLE PARALLEL SAFE STRICT
BEGIN ATOMIC
SELECT public.immutable_unaccent(regdictionary 'public.unaccent', $1);
END;

参见:

为 Postgres 9.5或更高版本删除这两个函数中的 PARALLEL SAFE

public是安装扩展的模式(缺省值为 public)。

显式类型声明(regdictionary)通过恶意用户使用该函数的重载变体来防御假设的攻击。

在此之前,我主张使用基于非重音模块附带的 STABLE函数 unaccent()的包装函式。关闭了 函数内联函数内联。这个版本执行的是 ABc3,而不是我之前提到的那个简单的包装函式。
这已经是在函数中添加 SET search_path = public, pg_temp的第一个版本的两倍——直到我发现字典也可以是模式限定的。尽管如此(Postgres 12)从文档中看不太明显。

如果 您缺乏创建 C 函数所需的特权,那么您将回到第二个最佳实现: 模块提供的 STABLE unaccent()函数的 IMMUTABLE函数包装器:

CREATE OR REPLACE FUNCTION public.f_unaccent(text)
RETURNS text
LANGUAGE sql IMMUTABLE PARALLEL SAFE STRICT AS
$func$
SELECT public.unaccent('public.unaccent', $1)  -- schema-qualify function and dictionary
$func$;

最后,表达式索引表达式索引进行查询 很快:

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'这样的非重音字符串。

更快的函数并不能转换为使用 表达式索引 的更快的查询。索引查找对预计算值进行操作,两种方式都非常快。但是索引维护和查询不使用索引的好处。像 位图索引扫描这样的访问方法可能必须访问堆中的 重新检查值(主关系) ,这涉及到执行底层函数。参见:

用 Postgres 10.3/9.6.8等加强了客户端程序的安全性。如在任何索引中使用时所示,您可以使用 需要对模式进行限定的函数和字典名称。参见:

捆绑

在 Postgres 9.5岁或以上中,像‘’或‘ ß’这样的连字必须手动展开(如果你需要的话) ,因为 unaccent()总是用一个 单身字母来代替:

SELECT unaccent('Œ Æ œ æ ß');


unaccent
----------
E A e a S

你会喜欢《邮差》中的 这个更新为非重音:

扩展 contrib/unaccent的标准 unaccent.rules文件以处理所有 Unicode 和 正确地扩大结扎(Thomas 门罗,莱昂纳多 · 贝内德蒂)

我强调一下,现在我们得到:

SELECT unaccent('Œ Æ œ æ ß');


unaccent
----------
OE AE oe ae ss

模式匹配

对于具有任意模式的 LIKEILIKE,将其与 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 索引比普通 B 树更昂贵(维护起来) :

对于左锚定模式有更简单的解决方案更多关于模式匹配和性能:

pg_trgm还提供了有用的 「相似性」(ABC1)及「距离」(<->)

Trigram 索引还支持简单的正则表达式(使用 ~等)和 大小写不敏感模式匹配(使用 ILIKE) :

不,PostgreSQL 不支持这种排序规则

PostgreSQL 不支持这样的排序规则(不管是否重音不敏感) ,因为除非二进制相等,否则比较不能返回相等的结果。这是因为在内部,它会为散列索引之类的东西引入许多复杂性。由于这个原因,排序在其严格意义上 只影响订货和不相等。

变通方法

取消重音词汇的全文搜索词典。

对于 FTS,您可以使用 unaccent定义自己的字典,

CREATE EXTENSION unaccent;


CREATE TEXT SEARCH CONFIGURATION mydict ( COPY = simple );
ALTER TEXT SEARCH CONFIGURATION mydict
ALTER MAPPING FOR hword, hword_part, word
WITH unaccent, simple;

然后您可以使用函数索引对其进行索引,

-- Just some sample data...
CREATE TABLE myTable ( myCol )
AS VALUES ('fóó bar baz'),('qux quz');


-- No index required, but feel free to create one
CREATE INDEX ON myTable
USING GIST (to_tsvector('mydict', myCol));

您现在可以非常简单地查询它

SELECT *
FROM myTable
WHERE to_tsvector('mydict', myCol) @@ 'foo & bar'


mycol
-------------
fóó bar baz
(1 row)

参见

没有口音。

unaccent模块也可以在没有 FTS 集成的情况下单独使用,这可以查看 欧文的回答