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

时间:2012-06-12 21:19:15

标签: sql postgresql localization indexing pattern-matching

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

的查询
SELECT * FROM users WHERE name LIKE 'João'

找到名称为Joao的行。

我知道可以使用unaccent_string contrib函数从PostgreSQL中删除字符串的重音,但我想知道PostgreSQL是否支持这些“重音不敏感”排序规则,因此上面的SELECT可以正常工作。

3 个答案:

答案 0 :(得分:168)

使用unaccent module - 这与您链接的内容完全不同。

  

unaccent是一个删除重音的文本搜索词典(变音符号   来自lexemes的迹象。

每个数据库安装一次:

CREATE EXTENSION unaccent;

如果收到如下错误:

  

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

在数据库服务器上安装contrib包,如相关答案中所述:

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

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

索引

要为此类查询使用索引,请创建index on the expression但是,Postgres只接受IMMUTABLE索引函数。如果函数可以为同一输入返回不同的结果,则索引可能会静默地中断。

unaccent()STABLE而非IMMUTABLE

不幸的是,unaccent()仅为STABLE,而非IMMUTABLE。根据{{​​3}},这是由于三个原因:

  1. 这取决于字典的行为。
  2. 此词典没有硬线连接。
  3. 因此,它还取决于当前search_path,它可以轻松改变。
  4. 网络上的

    this thread on pgsql-bugs指示将功能波动率改为IMMUTABLE。这种强力方法在某些条件下会破裂。

    其他人建议Some tutorials(就像我过去做过的那样)。

    关于是否明确声明使用过的字典的simple IMMUTABLE wrapper function IMMUTABLE,一直存在争议。阅读variant with two parametershere

    另一个替代方案是这个模块带有here,在Github上提供。没有自己测试过。我想我已经想出了 更好的主意

    现在最好

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

    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;

    public是您安装扩展程序的架构(public是默认设置)。

    以前,我已将SET search_path = public, pg_temp添加到函数中 - 直到我发现字典也可以是模式限定的IMMUTABLE unaccent() function by Musicbrainz。在我的第9.5行和第10页的测试中,这个版本有点短,大约快两倍。

    更新后的版本仍然不允许which is currently (pg 10) not documented,因为声明IMMUTABLE的函数可能不会调用正文中的非不可变函数来允许它。当我们在IMMUTABLE函数上使用 function inlining 时,对性能几乎无关紧要:

    CREATE INDEX users_unaccent_name_idx ON users(public.f_unaccent(name));
    

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

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

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

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

    连字

    在Postgres 9.5或更早中,必须手动扩展“Œ”或“ß”等连字(如果需要),因为unaccent()始终替换单< / em>信:

    SELECT unaccent('Œ Æ œ æ ß');
    
    unaccent
    ----------
    E A e a S
    

    你会喜欢Postgres中的'text search dictionary “unaccent” does not exist' entries in postgres log, supposedly during automatic analyze 9.6

      

    扩展contrib/unaccent的标准unaccent.rules文件以处理所有问题   Unicode已知的变音符号,正确扩展连字(Thomas   Munro,LéonardBenedetti)

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

    SELECT unaccent('Œ Æ œ æ ß');
    
    unaccent
    ----------
    OE AE oe ae ss
    

    模式匹配

    对于具有任意模式的 LIKE ILIKE ,请将其与PostgreSQL 9.1或更高版本中的模块this update to unaccent结合使用。创建一个三元组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还提供了有用的Pattern matching with LIKE, SIMILAR TO or regular expressions in PostgreSQL

    Trigram索引还支持使用~等的简单正则表达式。 不区分大小写的模式与ILIKE匹配:

答案 1 :(得分:2)

我非常确定PostgreSQL依赖底层操作系统进行整理。它 支持creating new collationscustomizing collations。不过,我不确定你可能会做多少工作。 (可能相当多。)

答案 2 :(得分:2)

不,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