PostgreSQL: comment rendre la requête «insensible à la casse»

339

Existe-t-il un moyen d'écrire des requêtes insensibles à la casse dans PostgreSQL, par exemple, je veux que les 3 requêtes suivantes retournent le même résultat.

SELECT id FROM groups where name='administrator'

SELECT id FROM groups where name='ADMINISTRATOR'

SELECT id FROM groups where name='Administrator'
Jame
la source
si citext est fourni avec votre installation Postgres, essayez le type citext. C'est un texte insensible à la casse
Michael Buen
2
Pour les nouveaux arrivants à cette question, ce lien vers la documentation officielle de postgres contient toutes les réponses données ici, ainsi que quelques autres options.
Parthian Shot
Monsieur, réaffectez la réponse acceptée à celle faite par @Arun, s'il vous plaît. C'est beaucoup moins compliqué et ne tirez pas beaucoup de problèmes après l'application.
zeliboba

Réponses:

451

Utilisez la fonction LOWER pour convertir les chaînes en minuscules avant de comparer.

Essaye ça:

SELECT id 
  FROM groups
 WHERE LOWER(name)=LOWER('Administrator')
Chandu
la source
92
Il est important de noter que l'utilisation de LOWER (ou de n'importe quelle fonction) sur les colonnes de prédicat - dans ce cas, "nom" - empêchera la recherche de tout index. S'il s'agit d'une table volumineuse ou fréquemment interrogée, cela pourrait causer des problèmes. Un classement insensible à la casse, un citext ou un index basé sur une fonction améliorera les performances.
Jordan
108
Ou créez simplement un index comme celui-ci: CREATE INDEX idx_groups_name ON groups lower (name);
Daniel
19
Spécifiez également varchar_pattern_opssi vous souhaitez que l'index fonctionne avec la LIKE 'xxx%'requête, c'est-à-dire CREATE INDEX ix_groups_name ON groups (lower(name) varchar_pattern_ops).
sayap
10
L'utilisation de l'opérateur ILIKE (comme indiqué dans les autres réponses ci-dessous) est une approche plus simple, même s'il s'agit de la réponse la plus votée.
Ryan
5
En parcourant les commentaires ici, beaucoup de suggestions suggèrent ici ILIKE, cela fonctionnera but with slow response,. Pour obtenir un accès rapide aux tableaux basés sur les résultats des calculs, je suggère à quiconque vérifie cela de bien vouloir accepter la réponse acceptée. Voir plus de détails ici et ici
Afolabi Olaoluwa Akinwumi
231

utiliser ILIKEau lieu deLIKE

SELECT id FROM groups WHERE name ILIKE 'Administrator'
Mohammad Reza Norouzi
la source
1
Notez que ce ILIKEn'est pas pris en charge par Hibernate lorsqu'il est utilisé dans Spring Boot.
AnT
@AnT il fonctionne avec org.hibernate.dialect.PostgreSQL94Dialectet Spring Boot 2.0.6.RELEASE. Mais IntelliJ s'en plaint.
Samintha Kaveesh
134

L'approche la plus courante consiste à minuscules ou majuscules la chaîne de recherche et les données. Mais cela pose deux problèmes.

  1. Il fonctionne en anglais, mais pas dans toutes les langues. (Peut-être même pas dans la plupart des langues.) Toutes les lettres minuscules n'ont pas de lettre majuscule correspondante; toutes les lettres majuscules n'ont pas de lettre minuscule correspondante.
  2. L'utilisation de fonctions comme lower () et upper () vous donnera un balayage séquentiel. Il ne peut pas utiliser d'index. Sur mon système de test, l'utilisation de lower () prend environ 2000 fois plus de temps qu'une requête qui peut utiliser un index. (Les données de test ont un peu plus de 100 000 lignes.)

Il existe au moins trois solutions moins fréquemment utilisées qui pourraient être plus efficaces.

  1. Utilisez le module citext , qui imite principalement le comportement d'un type de données insensible à la casse. Après avoir chargé ce module, vous pouvez créer un index insensible à la casse par CREATE INDEX ON groups (name::citext);. (Mais voir ci-dessous.)
  2. Utilisez un classement insensible à la casse. Ceci est défini lorsque vous initialisez une base de données. L'utilisation d'un classement insensible à la casse signifie que vous pouvez accepter à peu près n'importe quel format du code client, et vous retournerez toujours des résultats utiles. (Cela signifie également que vous ne pouvez pas effectuer de requêtes sensibles à la casse. Duh.)
  3. Créez un index fonctionnel. Créez un index en minuscules à l'aide de CREATE INDEX ON groups (LOWER(name));. Cela fait, vous pouvez profiter de l'index avec des requêtes comme SELECT id FROM groups WHERE LOWER(name) = LOWER('ADMINISTRATOR');, ou SELECT id FROM groups WHERE LOWER(name) = 'administrator';vous devez vous rappeler d'utiliser LOWER (), cependant.

Le module citext ne fournit pas un véritable type de données insensible à la casse. Au lieu de cela, il se comporte comme si chaque chaîne était en minuscule. Autrement dit, il se comporte comme si vous aviez appelé lower()chaque chaîne, comme dans le numéro 3 ci-dessus. L'avantage est que les programmeurs n'ont pas à se rappeler de mettre des chaînes en minuscules. Mais vous devez lire les sections "Comportement de comparaison de chaînes" et "Limitations" dans les documents avant de décider d'utiliser citext.

Mike Sherrill 'Cat Recall'
la source
1
À propos de # 1: Cela ne devrait pas être un problème, car ce serait deux chaînes différentes (pensez-y comme faire col = 'a'et col = 'b'). À propos de # 2: Comme vous l'avez dit, vous pouvez créer un index sur une expression, donc ce n'est pas vraiment un problème. Mais je suis d'accord avec vous que changer le classement est probablement la meilleure solution.
Vincent Savard
5
Quelqu'un peut-il me dire quels classements insensibles à la casse sont les classements intégrés de PostgreSQL? Je vois cela comme une option mais je ne trouve rien sur un classement insensible à la casse pour Postgres sur le net?
khorvat
1
@AnupShah: Non, je ne dis pas cela. Je n'exécute pas PostgreSQL sur Windows. Les documents 9.4 disent ceci : "Sur toutes les plates-formes, les classements nommés par défaut, C et POSIX sont disponibles. Des classements supplémentaires peuvent être disponibles en fonction de la prise en charge du système d'exploitation." Vous pouvez voir avec quels classements PostgreSQL pense qu'ils sont disponibles select * from pg_collation;.
Mike Sherrill 'Cat Recall'
1
@Matthieu: Ceci est la meilleure introduction (et prudence) au sujet que je connais: les cas Edge à garder à l'esprit. Partie 1 - Texte .
Mike Sherrill 'Cat Recall'
1
@Matthieu: La FAQ Unicode est également amusante à lire. Voici pourquoi il n'y a pas de caractère majuscule unique pour. . .
Mike Sherrill 'Cat Recall'
95

Vous pouvez utiliser ILIKE. c'est à dire

SELECT id FROM groups where name ILIKE 'administrator'
ADJ
la source
C'est correct et fonctionne bien pour moi, j'utilise MAC OS X (Mountain Lion).
ADJ
5
Cela fonctionnera, mais avec une réponse lente. Pour obtenir un accès rapide aux tables basées sur les résultats des calculs, je suggère d'utiliser la lowerfonction. Voir plus de détails
Afolabi Olaoluwa Akinwumi
1
@AfolabiOlaoluwaAkinwumi fondamentalement, cela revient à savoir si vous recherchez des résultats opposés au filtrage des valeurs connues . Dans ce dernier cas, un seul cas uniforme doit être conservé au niveau des données permettant à l'opérateur d'égalité de fonctionner. [La recommandation personnelle est en majuscule pascale pour les valeurs de code de type]
Chris Marisic
53

Vous pouvez également lire le ILIKEmot - clé. Il peut parfois être très utile, bien qu'il ne soit pas conforme à la norme SQL. Voir ici pour plus d'informations: http://www.postgresql.org/docs/9.2/static/functions-matching.html

Priidu Neemre
la source
9
Il faut faire attention aux entrées malveillantes des utilisateurs. Si vous exécutez une requête comme email ILIKE 'user-input-email-here', assurez-vous d'échapper à l'entrée utilisateur. Sinon, les gens peuvent entrer des caractères comme% qui correspondent à n'importe quoi.
Matt De Leon
2
@MattDeLeon Salut. Bien dit. Mais je veux juste vous demander si j'utilise ILIKEet prepared statementscela me protégera- sql injectiont-il?
slevin
Pas sûr, je suppose que vous voulez envoyer une chaîne d'échappement à l'instruction préparée.
Matt De Leon
1
"Le mot clé ILIKE peut être utilisé à la place de LIKE pour rendre la correspondance insensible à la casse en fonction des paramètres régionaux actifs. Ce n'est pas dans la norme SQL mais c'est une extension PostgreSQL." Fonctionne comme un charme en 9.3
Aleksey Deryagin
1
ILIKE est plus lent que lower(column_name) like %expression%.
Patryk Imosa
28

Vous pouvez également utiliser des expressions régulières POSIX, comme

SELECT id FROM groups where name ~* 'administrator'

SELECT 'asd' ~* 'AsD' Retour t

James Brown
la source
1
J'ai eu le même problème, j'avais besoin de recherches insensibles à la casse sur ma base de données PostgreSQL. J'ai pensé à transformer la chaîne d'entrée utilisateur en une expression régulière. Maintenant, utiliser ~ * au lieu de = ou LIKE a parfaitement fonctionné! Je n'avais pas besoin de créer de nouveaux index, colonnes ou autre. Bien sûr, la recherche d'expression régulière est plus lente que la comparaison d'octets simples, mais je ne pense pas que l'impact sur les performances serait beaucoup plus important que de devoir gérer deux ensembles de données (un inférieur ou en majuscule uniquement pour la recherche, puis avoir à récupérer l'original correspondant données de l'autre ensemble). En plus, c'est plus propre!
Cyberknight
1
D'accord, mais comment faire avec regexp_matches () par exemple?
WKT
Selon les documents postgres: l'opérateur ~~ est équivalent à LIKE, et ~~ * correspond à ILIKE. Il existe également des opérateurs! ~~ et! ~~ * qui représentent respectivement NOT LIKE et NOT ILIKE. Tous ces opérateurs sont spécifiques à PostgreSQL.
sh4
J'ai rencontré un problème lorsque des crochets sont inclus dans le texte, cela ne fonctionne pas. comme: "code (LC)"
Oshan Wisumperuma
8

L'utilisation ~*peut améliorer considérablement les performances, avec les fonctionnalités de INSTR.

SELECT id FROM groups WHERE name ~* 'adm'

renvoie des lignes dont le nom contient OU égal à «adm».

Robin Goh
la source
1
Hé, Robin, bienvenue à SO. La réponse de James Brown proposait déjà cette solution. De plus, la réponse que vous proposez ne tire aucun parti de l'expression régulière.
Rafael