PostgreSQL
 sql >> Base de données >  >> RDS >> PostgreSQL

PostgreSQL prend-il en charge les classements insensibles aux accents ?

Utilisez le module sans accent pour cela - qui est complètement différent de ce vers quoi vous créez un lien.

unaccent est un dictionnaire de recherche de texte qui supprime les accents (diacritiques) des lexèmes.

Installer une fois par base de données avec :

CREATE EXTENSION unaccent;

Si vous obtenez une erreur comme :

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

Installez le package contrib sur votre serveur de base de données comme indiqué dans cette réponse connexe :

  • Erreur lors de la création d'une extension sans accent sur PostgreSQL

Il fournit entre autres 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 type de requête, créez un index sur l'expression. Cependant , Postgres n'accepte que IMMUTABLE fonctions pour les index. Si une fonction peut renvoyer un résultat différent pour la même entrée, l'index peut casser silencieusement.

unaccent() uniquement STABLE non IMMUTABLE

Malheureusement, unaccent() est seulement 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 search_path actuel , qui peut changer facilement.

Certains tutoriels sur le Web demandent de modifier simplement la volatilité de la fonction en IMMUTABLE . Cette méthode de force brute peut casser sous certaines conditions.

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

Il y a un débat en cours sur l'opportunité de rendre la variante avec deux paramètres IMMUTABLE qui déclare explicitement le dictionnaire utilisé. Lire ici ou ici.

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

Meilleur pour le moment

Cette approche est plus efficace que d'autres solutions flottantes, et plus sûre .
Créer un IMMUTABLE Fonction wrapper SQL exécutant la forme à deux paramètres avec une fonction et un dictionnaire qualifiés de schéma câblé.

Étant donné que l'imbrication d'une fonction non immuable désactiverait l'inlining de la fonction, basez-la sur une copie de la fonction C, (faux) déclaré IMMUTABLE aussi bien. C'est seulement purpose doit être utilisé dans l'encapsuleur de fonction SQL. N'est pas destiné à être utilisé seul.

La sophistication est nécessaire car il n'y a aucun moyen de câbler le dictionnaire dans la déclaration de la fonction C. (Il faudrait pirater le code C lui-même.) La fonction wrapper SQL le fait et permet à la fois la fonction inlining et index d'expression.

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

Supprimez PARALLEL SAFE des deux fonctions pour Postgres 9.5 ou antérieur.

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

La déclaration de type explicite (regdictionary ) protège contre les attaques hypothétiques avec des variantes surchargées de la fonction par des utilisateurs malveillants.

Auparavant, je préconisais une fonction wrapper basée sur le STABLE fonction unaccent() livré avec le module unaccent. Cette fonction désactivée inlining. Cette version s'exécute dix fois plus vite que la simple fonction wrapper que j'avais ici plus tôt.
Et c'était déjà deux fois plus rapide que la première version qui ajoutait SET search_path = public, pg_temp à la fonction - jusqu'à ce que je découvre que le dictionnaire peut également être qualifié de schéma. Toujours (Postgres 12) pas trop évident à partir de la documentation.

Si vous n'avez pas les privilèges nécessaires pour créer des fonctions C, vous êtes de retour à la deuxième meilleure implémentation :Un IMMUTABLE wrapper de fonction autour de STABLE unaccent() fonction fournie par le module :

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;

Enfin, l'index d'expression pour effectuer des requêtes rapides :

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

N'oubliez pas de recréer les index impliquant cette fonction après toute modification de la fonction ou du dictionnaire, comme une mise à niveau de version majeure sur place qui ne recréerait pas les index. Les versions majeures récentes avaient toutes des mises à jour pour le unaccent module.

Adaptez les requêtes pour qu'elles correspondent à l'index (afin que le planificateur de requêtes l'utilise) :

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

Vous n'avez pas besoin de la fonction dans la bonne expression. Là, vous pouvez également fournir des chaînes sans accent comme 'Joao' directement.

La fonction plus rapide ne se traduit pas par des requêtes beaucoup plus rapides à l'aide de l'index d'expression . Cela fonctionne sur des valeurs pré-calculées et est déjà très rapide. Mais la maintenance de l'index et les requêtes n'utilisant pas l'avantage de l'index.

La sécurité des programmes clients a été renforcée avec Postgres 10.3 / 9.6.8 etc. Vous avez besoin pour qualifier la fonction de schéma et le nom du dictionnaire, comme illustré lorsqu'ils sont utilisés dans tous les index. Voir :

  • "le dictionnaire de recherche de texte "unaccent" n'existe pas" entrées dans le journal postgres, soi-disant lors de l'analyse automatique

Ligatures

Dans Postgres 9.5 ou antérieur les ligatures comme 'Œ' ou 'ß' doivent être développées manuellement (si vous en avez besoin), car unaccent() remplace toujours un simple lettre :

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

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

Vous allez adorer cette mise à jour pour unaccent dans Postgres 9.6 :

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

Bold emphase mienne. Nous obtenons maintenant :

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

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

Correspondance de modèle

Pour LIKE ou ILIKE avec des modèles arbitraires, combinez ceci avec le module pg_trgm dans PostgreSQL 9.1 ou version ultérieure. Créez un trigramme GIN (généralement préférable) ou un index d'expression GIST. 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 telles que :

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

Les index GIN et GIST sont plus chers à maintenir que plain btree :

  • Différence entre l'indice GiST et GIN

Il existe des solutions plus simples pour les modèles simplement ancrés à gauche. En savoir plus sur la correspondance de modèles et les performances :

  • Mise en correspondance de modèles avec LIKE, SIMILAR TO ou des expressions régulières dans PostgreSQL

pg_trgm fournit également des opérateurs utiles pour la "similarité" (% ) et "distance" (<-> ).

Les index trigrammes prennent également en charge les expressions régulières simples avec ~ et coll. et insensible à la casse correspondance de modèle avec ILIKE :

  • Accent PostgreSQL + recherche insensible à la casse