Est-ce que PostgreSQL supporte les collations" insensibles à l'accent"?

dans Microsoft SQL Server, il est possible de spécifier une collation "insensible à l'accent" (pour une base de données, une table ou une colonne), ce qui signifie qu'il est possible pour une requête comme

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

pour trouver une ligne avec un nom Joao .

je sais qu'il est possible d'enlever les accents des cordes dans PostgreSQL en utilisant la fonction uncent_string contrib, mais je me demande si PostgreSQL supporte ces "accents" "collations insensibles de sorte que le SELECT ci-dessus fonctionnerait.

75
demandé sur Erwin Brandstetter 2012-06-13 01:19:14

3 réponses

utilisez le module inexistant pour cela - qui est complètement différent de ce que vous liez à.

unaccent est une recherche de texte dictionnaire qui supprime les accents diacritiques des signes) à partir de lexèmes.

installer une fois par base de données avec:

CREATE EXTENSION unaccent;

si vous obtenez une erreur comme:

Erreur: Impossible d'ouvrir le fichier de contrôle d'extension "/usr / share/postgresql / 9.x / extension / absence.de contrôle": Aucun fichier ou répertoire

installez le paquet contrib sur votre serveur de base de données comme indiqué dans cette réponse:

entre autres, il fournit la fonction unaccent() vous pouvez utiliser avec votre exemple (où LIKE ne semble pas nécessaire).

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

Index

pour utiliser un index pour ce genre de requête, créez un index sur l'expression . cependant , Postgres n'accepte que les fonctions IMMUTABLE pour les index. Si une fonction peut renvoyer un résultat différent pour la même entrée, l'indice pourrait silencieusement pause.

unaccent() seulement STABLE pas IMMUTABLE

malheureusement, unaccent() n'est que STABLE , pas IMMUTABLE . Selon ce fil sur pgsql-bugs , cela est dû à trois raisons:

  1. Cela dépend du comportement d'un dictionnaire.
  2. Il n'y a pas de connexion câblée à ce dictionnaire.
  3. cela dépend donc aussi du courant search_path , qui peut changer facilement.

quelques tutoriels sur le web donnent pour instruction de simplement modifier la volatilité de la fonction à IMMUTABLE . Cette méthode de la force brute peut se briser sous certaines conditions.

D'autres suggèrent un simple IMMUTABLE fonction de wrapper (comme je l'ai fait moi-même dans le passé).

Il y a un débat en cours si le variante avec deux paramètres IMMUTABLE qui déclare explicitement le dictionnaire utilisé. Lire ici ou ici .

une autre alternative serait ce module avec une fonction immuable unaccent() par Musicbrainz , fournie sur Github. N'ai pas testé moi-même. Je pense que j'ai trouvé un meilleure idée :

le Meilleur pour l'instant

je propose une approche qui est au moins aussi efficace que d'autres solutions flottant autour, mais plus sûr : Créer une fonction de wrapper avec la forme à deux paramètres et "dur-wire" le schéma pour la fonction et le dictionnaire:

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

public étant le schéma où vous avez installé l'extension ( public est la valeur par défaut).

Auparavant, j'avais ajouté SET search_path = public, pg_temp à la fonction - jusqu'à ce que je découvre que le dictionnaire peut être qualifié de schéma, aussi, qui est actuellement (pg 10) non documenté . Cette version est un peu plus courte et environ deux fois plus rapide dans mes tests sur pg 9.5 et pg 10.

la version mise à jour ne permet toujours pas fonction en ligne parce que les fonctions déclarées IMMUTABLE ne peuvent pas appeler des fonctions non immuables dans le corps pour le faire. Peu importe pour la performance alors que nous faisons usage d'un index d'expression sur ce IMMUTABLE fonction:

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

la sécurité pour les programmes clients a été renforcée avec Postgres 10.3 / 9.6.8 etc. Vous besoin à la fonction de schéma-qualifier et le dictionnaire comme démontré lorsqu'il est utilisé dans tous les index. Voir:

Adaptez vos requêtes pour correspondre à l'index (de sorte que le planificateur de requêtes peut l'utiliser):

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

vous n'avez pas besoin de la fonction dans la bonne expression. Vous pouvez fournir des cordes sans accent comme 'Joao' directement.

Ligatures

Postgres 9.5 ou plus anciennes ligatures comme "Œ" ou "ß" doivent être étendues manuellement( si vous avez besoin de cela), depuis unaccent() remplace toujours un simple lettre:

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

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

Vous aimerez cette mise à jour pour unaccent dans Postgres 9.6 :

Étendre contrib/unaccent standard unaccent.rules fichier pour gérer toutes les diacritiques connus à Unicode, et développer correctement les ligatures (Thomas Munro, Léonard Benedetti)

le Gras c'est moi qui souligne. Maintenant nous avons:

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

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

Pattern matching

pour LIKE ou ILIKE avec des motifs arbitraires, combiner avec le module pg_trgm en PostgreSQL 9.1 ou plus tard. Créer un trigramme GIN (généralement préférable) ou GIST indice d'expression. Exemple pour GIN:

CREATE INDEX users_unaccent_name_trgm_idx ON users
USING gin (f_unaccent(name) gin_trgm_ops);

peut être utilisé pour des requêtes comme:

SELECT * FROM users
WHERE  f_unaccent(name) LIKE ('%' || f_unaccent('João') || '%');

GIN et les index GIST sont plus coûteux à entretenir que la plaine btree:

il existe des solutions plus simples pour les motifs ancrés à gauche. Plus sur le modèle correspondance et de la performance:

pg_trgm fournit également utile opérateurs pour" similitude "( % ) et "distance" ( <-> ) .

Trigramme indices soutien expressions régulières simples avec ~ et al. et l'affaire insensible correspondance avec ILIKE :

149
répondu Erwin Brandstetter 2018-08-13 08:36:26

Je suis presque sûr que PostgreSQL s'appuie sur le système d'exploitation sous-jacent pour la compilation. ne support la création de nouveaux classements , et personnalisation des classements . Je ne suis pas sûr que ce soit beaucoup de travail pour vous, cependant. (Peut-être beaucoup.)

2
répondu Mike Sherrill 'Cat Recall' 2012-06-12 21:54:48

Non, PostgreSQL ne prend pas en charge les classements dans ce sens

PostgreSQL ne supporte pas les collations comme celle-ci (insensible à l'accent ou non) parce qu'aucune comparaison ne peut retourner égale à moins que les choses soient binaires-égales. C'est parce qu'à l'interne, il introduirait beaucoup de complexités pour des choses comme un indice de hachage. C'est la raison pour laquelle les recueils au sens strict ne concernent que l'ordonnance et non l'égalité.

Solutions de contournement

Plein-Texte-Recherche Dictionnaire Unaccents lexèmes.

pour FTS, vous pouvez définir votre propre dictionnaire en utilisant 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;

que vous pouvez ensuite indexer avec un index fonctionnel,

-- 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));

vous pouvez maintenant l'interroger très simplement

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

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

Voir aussi

ne se rend pas compte par lui-même.

le unaccent module peut également être utilisé par lui-même sans fts-intégration, pour ce check out réponse D'Erwin

1
répondu Evan Carroll 2018-05-30 01:52:08