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

gopyfrb3  于 2022-11-29  发布在  PostgreSQL
关注(0)|答案(3)|浏览(160)

在Microsoft SQL Server中,可以指定“不区分重音”排序规则(用于数据库、表或列),这意味着可以对

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

查找名称为Joao行。
我知道在PostgreSQL中使用unaccent_string contrib函数可以去除字符串中的重音,但我想知道PostgreSQL是否支持这些“不区分重音”的排序规则,以便上面的SELECT可以工作。

byqmnocz

byqmnocz1#

更新Postgres 12或更高版本

Postgres 12增加了非确定性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;

fiddle
详细信息请阅读手册。这个blog post by Laurenz Albe可能有助于理解。
但是ICU排序也有缺点。手册:
[...]它们也有一些缺点。最重要的是,使用它们会导致性能下降。请特别注意,B树不能对使用不确定排序规则的索引使用重复数据删除。此外,某些操作(如模式匹配操作)不能使用不确定排序规则。因此,它们应仅在特别需要的情况下使用。
我的“遗留”解决方案可能仍然更优越:

适用于所有版本

使用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');

索引

    • 但是**,Postgres只接受IMMUTABLE函数作为索引。如果函数可以为相同的输入返回不同的结果,则索引可能会自动中断。

unaccent()仅限STABLE而非IMMUTABLE

不幸的是,unaccent()只是STABLE,而不是IMMUTABLE。根据pgsql-bugs上的这个线程,这是由于 * 三 * 个原因:
1.这取决于字典的行为。
1.这本词典没有硬连线连接。
1.因此,它还取决于电流search_path,电流search_path很容易改变。
web上的Some tutorials指示只将函数volatile更改为IMMUTABLE。这种暴力方法在某些情况下可能会崩溃。
其他人建议使用一个简单的IMMUTABLE Package 函数(就像我自己过去做的那样)。
关于是否使用两个参数IMMUTABLE来显式声明所使用的字典,目前还存在争议。
另一个替代方案是Github上提供的IMMUTABLE unaccent() function by Musicbrainz模块。我自己还没有测试过。我想我已经想出了一个***更好的主意***:

目前最佳

这种方法比其他浮动解决方案更高效,也更安全
创建一个IMMUTABLE SQL Package 函数,该函数使用硬连接的模式限定函数和字典执行双参数表单。
由于嵌套一个非不可变函数会禁用函数内联,因此将其基于C函数的副本,(fake)声明为IMMUTABLE。它的 * 唯一 * 用途是用于SQL函数 Package 。而不是用于它自己。
由于没有办法在C函数的声明中硬连接字典,因此需要复杂性。(这需要破解C代码本身。)SQL Package 函数可以做到这一点,并允许函数内联 * 和 * 表达式索引。

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)可以防御恶意用户使用函数的重载变体进行的假设攻击。

  • 以前,我提倡一个基于unaccent模块附带的STABLE函数unaccent()的 Package 函数。它禁用了function inlining。这个版本的执行速度比我之前使用的简单 Package 函数快十倍

这已经是第一个版本的两倍了,第一个版本将SET search_path = public, pg_temp添加到函数中--直到我发现字典也可以是模式限定的。

如果您缺少创建C函数所需的权限,则返回到次佳实现:IMMUTABLE函数 Package 函数,用于封装模块提供的STABLEunaccent()函数:

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

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

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

  • '文本搜索字典“unaccent”不存在' postgres日志中条目,可能在自动分析期间

连字

在Postgres**9.5或更早版本中,**连字如''或'ß'必须手动扩展(如果需要),因为unaccent()总是替换 * 单个 * 字母:

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

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

你会喜欢这个更新,以不强调在Postgres9.6
扩展contrib/unaccent的标准unaccent.rules文件以处理Unicode已知的所有音调符号,并正确扩展连字(托马斯Munro,Léonard Benedetti)
大胆强调我的。现在我们得到:

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树更高:

  • GiST和GIN指数之间的差异

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

pg_trgm还为“相似性”(%)和“距离”(<->)提供了有用的运算符。
三元组索引还支持~等的简单正则表达式和ILIKE不区分大小写模式匹配:

  • PostgreSQL重音+不区分大小写搜索
1sbrub3j

1sbrub3j2#

否,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 module也可单独使用,无需集成FTS,请查看Erwin's answer

kwvwclae

kwvwclae3#

我很确定PostgreSQL依赖于底层操作系统进行排序。它 * 确实 * 支持creating new collationscustomizing collations。不过我不确定这对你来说会有多少工作。(可能会相当多。)

相关问题