PostgreSQL是否支持"不区分重音"排序规则?

Dan*_*dio 88 sql postgresql indexing localization pattern-matching

在Microsoft SQL Server中,可以指定"重音不敏感"排序规则(对于数据库,表或列),这意味着可以进行类似的查询

SELECT * FROM users WHERE name LIKE 'João'
Run Code Online (Sandbox Code Playgroud)

找到一个带有Joao名字的行.

我知道可以使用unaccent_string contrib函数从PostgreSQL中删除字符串中的重音符号,但我想知道PostgreSQL是否支持这些"重音不敏感"排序规则,以便SELECT上述方法可行.

Erw*_*ter 179

使用unaccent模块 - 这与您链接的完全不同.

unaccent是一个文本搜索字典,用于删除lexemes中的重音符号(变音符号).

每个数据库安装一次:

CREATE EXTENSION unaccent;
Run Code Online (Sandbox Code Playgroud)

如果您收到如下错误:

错误:无法打开扩展控制文件"/usr/share/postgresql/9.x/extension/unaccent.control":没有这样的文件或目录

按照此相关答案中的说明在数据库服务器上安装contrib包:

除此之外,它还提供了unaccent()可以与您的示例一起使用的功能(LIKE似乎不需要).

ERROR: could not open extension control file
"/usr/share/postgresql/<version>/extension/unaccent.control": No such file or directory
Run Code Online (Sandbox Code Playgroud)

指数

要为该类查询使用索引,请在表达式上创建索引.但是,Postgres只接受IMMUTABLE索引函数.如果函数可以为同一输入返回不同的结果,则索引可能会无声地中断.

unaccent()只是STABLE没有IMMUTABLE

不幸的是,unaccent()只是STABLE,不是IMMUTABLE.根据pgsql-bugs上的这个帖子,这是由于三个原因:

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

网上的一些教程指示只是将函数波动率改为IMMUTABLE.这种强力方法在某些条件下会破裂.

其他人提出了一个简单的IMMUTABLE包装函数(就像我过去自己做的那样).

关于是否使用两个参数 IMMUTABLE来明确声明使用的字典,一直存在争议.在这里这里阅读.

另一个替代方案是这个模块具有MusicbrainzIMMUTABLE unaccent()功能,在Github上提供.没有自己测试过.我想我已经提出了一个更好的主意:

现在最好

我提出的方法至少与其他解决方案一样有效,但更安全:使用双参数形式创建包装函数,并使用"硬连线"函数和字典的模式:

SELECT *
FROM   users
WHERE  unaccent(name) = unaccent('João');
Run Code Online (Sandbox Code Playgroud)

IMMUTABLE是您安装扩展的架构(IMMUTABLE是默认设置).

以前,我已经添加PARALLEL SAFE到函数中 - 直到我发现字典也可以是模式限定的,这是当前(第10页)没有记录.在我的第9.5行和第10页的测试中,这个版本有点短,大约快两倍.

更新后的版本仍然不允许函数内联,因为声明的函数public可能不会调用正文中的非不可变函数来允许它.当我们在这个函数上使用表达式索引时,对性能很重要public:

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$;
Run Code Online (Sandbox Code Playgroud)

使用Postgres 10.3/9.6.8等来加强客户端程序的安全性.在任何索引中使用时,您需要对函数和字典进行模式限定.看到:

调整您的查询以匹配索引(以便查询计划程序可以使用它):

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;
Run Code Online (Sandbox Code Playgroud)

您不需要正确表达式中的函数.您可以regdictionary直接提供非重音字符串.

连字

在Postgres的9.5或以上的连字像"O E"或"SS"都需手动展开(如果你需要的),因为STABLE总是一个替代单一字母:

CREATE INDEX users_unaccent_name_idx ON users(public.f_unaccent(name));
Run Code Online (Sandbox Code Playgroud)

你会喜欢这个更新,以便在Postgres 9.6中使用unaccent :

扩展unaccent()标准SET search_path = public, pg_temp文件以处理Unicode已知的所有变音符号,并正确扩展连字(Thomas Munro,LéonardBenedetti)

大胆强调我的.现在我们得到:

SELECT * FROM users
WHERE  f_unaccent(name) = f_unaccent('João');
Run Code Online (Sandbox Code Playgroud)

模式匹配

对于IMMUTABLESTABLE使用任意模式,将其与unaccent()PostgreSQL 9.1或更高版本中的模块结合使用.创建一个三元组GIN(通常是可取的)或GIST表达式索引.GIN的示例:

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

unaccent
----------
E A e a S
Run Code Online (Sandbox Code Playgroud)

可用于以下查询:

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

unaccent
----------
OE AE oe ae ss
Run Code Online (Sandbox Code Playgroud)

GIN和GIST索引的维护成本比普通btree要贵:

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

unaccent为"相似性"('Joao')和"距离"(unaccent())提供了有用的运算符.

Trigram索引也支持简单的正则表达式contrib/unaccent等.和不区分大小写的模式匹配unaccent.rules:

  • 你的答案和Postgres的文档一样好:惊人的! (5认同)
  • @a_horse_with_no_name:我还没有时间测试它,但这是一个预期的用例。 (2认同)
  • 这个非常有用的答案拯救了我本周的工作。我会将其添加到 @Erwin Brandstetter 策划的答案(长)列表中,这些答案已成为我的 Postgres 圣经。谢谢你!对于这个和所有其他的。 (2认同)

Eva*_*oll 9

不,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;
Run Code Online (Sandbox Code Playgroud)

然后您可以使用功能索引对其进行索引,

-- 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));
Run Code Online (Sandbox Code Playgroud)

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

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

    mycol    
-------------
 fóó bar baz
(1 row)
Run Code Online (Sandbox Code Playgroud)

也可以看看

本身不重音。

unaccent模块也可以在没有 FTS 集成的情况下单独使用,为此请查看Erwin 的答案

  • 请注意,从 Postgres 12 开始,这里的开头段落不再严格正确,引入了[非确定性排序规则](https://www.postgresql.org/docs/current/collat​​ion.html#COLLATION-NONDETERMINISTIC)。然而,它们仍然不受模式匹配运算符的支持。 (2认同)