429 votes

PostgreSQL : Comment faire une requête "insensible à la casse" ?

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 renvoient 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'

0 votes

Si citext est fourni avec votre installation Postgres, essayez le type citext. C'est un texte insensible à la casse

2 votes

Pour les nouveaux venus à cette question, ce lien La documentation officielle de postgres contient toutes les réponses données ici, ainsi que quelques autres options.

1 votes

Monsieur, réassignez la réponse acceptée à celle faite par @Arun s'il vous plaît. Elle est beaucoup moins compliquée et n'entraîne pas un tas de problèmes après l'application.

561voto

Chandu Points 40028

Utilisez INFÉRIEURE pour convertir les chaînes de caractères en minuscules avant de les comparer.

Essayez ça :

SELECT id 
  FROM groups
 WHERE LOWER(name)=LOWER('Administrator')

117 votes

Il est important de noter que l'utilisation de LOWER (ou de toute autre fonction) sur les colonnes de prédicat - dans ce cas "nom" - fera que les index ne pourront plus être recherchés. S'il s'agit d'une table importante ou fréquemment interrogée, cela peut poser problème. La collation insensible à la casse, le citext, ou un index basé sur une fonction amélioreront les performances.

129 votes

Ou simplement créer un index comme ceci : CREATE INDEX idx_groups_name ON groups lower(name) ;

22 votes

Précisez également varchar_pattern_ops si vous voulez que l'index fonctionne avec LIKE 'xxx%' la requête, c'est-à-dire CREATE INDEX ix_groups_name ON groups (lower(name) varchar_pattern_ops) .

349voto

En utilisant ILIKE au lieu de LIKE

SELECT id FROM groups WHERE name ILIKE 'Administrator'

7 votes

Notez que ILIKE n'est pas pris en charge par Hibernate lorsqu'il est utilisé dans Spring Boot.

1 votes

@AnT il fonctionne avec org.hibernate.dialect.PostgreSQL94Dialect et Spring Boot 2.0.6.RELEASE. Mais IntelliJ s'en plaint.

149voto

L'approche la plus courante consiste à mettre en minuscules ou en 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). Chaque lettre minuscule ne correspond pas à une lettre lettre majuscule correspondante ; chaque lettre majuscule n'a pas une lettre lettre minuscule correspondante.
  2. En utilisant des fonctions comme lower() et upper(), vous obtiendrez un résultat séquentiel. séquentiel. Il ne peut pas utiliser d'index. Sur mon système de test, l'utilisation de lower() prend environ 2000 fois plus long qu'une requête qui peut utiliser un index. (Les données de test ont un peu plus de 100k lignes).

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

  1. Utilisez le module citext qui imite le plus souvent 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 en utilisant la méthode suivante CREATE INDEX ON groups (name::citext); . (Mais voir ci-dessous.)
  2. Utilisez une collation insensible à la casse. Ce paramètre est défini lorsque vous initialisez une base de données. L'utilisation d'une collation insensible à la casse signifie que vous pouvez accepter accepter à peu près n'importe quel format de code client, et vous retournerez toujours résultats utiles. (Cela signifie également que vous ne pouvez pas faire de requêtes sensibles à la casse. Duh.)
  3. Créez un index fonctionnel. Créez un index minuscule en utilisant CREATE INDEX ON groups (LOWER(name)); . Après avoir fait cela, 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 souvenez-vous de 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. C'est-à-dire qu'il se comporte comme si vous aviez appelé lower() sur chaque corde, comme dans le numéro 3 ci-dessus. L'avantage est que les programmeurs n'ont pas à se souvenir de mettre les chaînes en minuscules. Mais vous devez lire les sections "String Comparison Behavior" et "Limitations" dans la documentation avant de décider d'utiliser citext.

1 votes

À propos du point 1 : Il ne devrait pas y avoir de problème, puisqu'il s'agirait de deux chaînes différentes (pensez-y comme si vous faisiez col = 'a' y col = 'b' ). A 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 la collation est très probablement la meilleure solution.

0 votes

Avez-vous entendu quelqu'un dire que citext ne fonctionne pas avec postgresql jdbc (hibernate) ?

0 votes

@BillR : Non, mais je n'utilise pas moi-même JDBC avec PostgreSQL, donc je pourrais facilement négliger un tel problème. Il y a ont a été quelques bogues dans les mises à jour de version aux bases de données en utilisant citext.

57voto

Priidu Neemre Points 483

Vous pouvez également vous renseigner sur le ILIKE mot-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

10 votes

Il faut se méfier des entrées malveillantes de l'utilisateur. Si vous exécutez une requête comme email ILIKE 'user-input-email-here' assurez-vous d'échapper à l'entrée de l'utilisateur. Sinon, les gens peuvent entrer des caractères comme % qui correspondent à n'importe quoi.

2 votes

@MattDeLeon Salut. Bien dit. Mais je veux juste te demander, si j'utilise ILIKE y prepared statements Est-ce que cela me protégera de sql injection ?

0 votes

Je ne suis pas sûr, je suppose que vous voulez envoyer une chaîne d'échappement à l'instruction préparée.

35voto

James Brown Points 32

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

SELECT id FROM groups where name ~* 'administrator'

SELECT 'asd' ~* 'AsD' renvoie à t

1 votes

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 de saisie de l'utilisateur en une expression régulière. Maintenant, l'utilisation de ~* au lieu de = ou LIKE a fonctionné parfaitement ! Je n'ai pas eu besoin de créer de nouveaux index, colonnes ou autres. Bien sûr, la recherche par expression régulière est plus lente que la comparaison directe d'octets, mais je ne pense pas que l'impact sur les performances soit tellement plus important que de devoir gérer deux ensembles de données (un en minuscules ou en majuscules uniquement pour la recherche, puis de devoir récupérer les données originales correspondantes dans l'autre ensemble). De plus, c'est plus propre !

1 votes

Bien, mais comment faire avec regexp_matches() par exemple ?

2 votes

Selon la documentation de Postgres : L'opérateur ~~ est équivalent à LIKE, et * correspond à ILIKE. Il existe également les opérateurs ! et !~~* qui représentent respectivement NOT LIKE et NOT ILIKE. Tous ces opérateurs sont spécifiques à PostgreSQL.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X