web-dev-qa-db-fra.com

Comment écrivez-vous une requête insensible à la casse pour MySQL et Postgres?

J'exécute une base de données MySQL localement pour le développement, mais je déploie sur Heroku qui utilise Postgres. Heroku gère presque tout, mais mes instructions Like insensibles à la casse deviennent sensibles à la casse. Je pourrais utiliser des instructions iLike, mais ma base de données MySQL locale ne peut pas gérer cela.

Quelle est la meilleure façon d'écrire une requête insensible à la casse compatible avec MySQL et Postgres? Ou dois-je écrire des instructions Like et iLike distinctes en fonction de la base de données à laquelle mon application parle?

64
DA.
select * from foo where upper(bar) = upper(?);

Si vous définissez le paramètre en majuscules dans l'appelant, vous pouvez éviter le deuxième appel de fonction.

58
Paul Tomblin

La morale de cette histoire est la suivante: n'utilisez pas une pile logicielle différente pour le développement et la production. Jamais.

Vous allez juste vous retrouver avec des bugs que vous ne pouvez pas reproduire en dev; vos tests seront sans valeur. Ne le fais pas.

Il est hors de question d'utiliser un moteur de base de données différent - il y aura BEAUCOUP plus de cas où il se comporte différemment que LIKE (aussi, avez-vous vérifié les classements utilisés par les bases de données? Sont-ils identiques dans CHAQUE CAS? Sinon, vous pouvez oublier ORDER BY sur les colonnes varchar fonctionnant de la même manière)

73
MarkR

Utilisez Arel:

Author.where(Author.arel_table[:name].matches("%foo%"))

matches utilisera l'opérateur ILIKE pour Postgres et LIKE pour tout le reste.

36
jswanner

En postgres, vous pouvez le faire:

SELECT whatever FROM mytable WHERE something ILIKE 'match this';

Je ne sais pas s'il existe un équivalent pour MySQL, mais vous pouvez toujours le faire, ce qui est un peu moche mais devrait fonctionner à la fois dans MySQL et postgres:

SELECT whatever FROM mytable WHERE UPPER(something) = UPPER('match this');
13
Adam Pierce

Il y a plusieurs réponses, dont aucune n'est très satisfaisante.

  • LOWER (bar) = LOWER (?) sera fonctionne sur MySQL et Postgres, mais est susceptible de perform terriblement sur MySQL: MySQL n'utilisera pas ses index à cause de la fonction LOWER. Sur Postgres, vous pouvez ajouter un index fonctionnel (sur LOWER (bar) ) mais MySQL ne le supporte pas.
  • MySQL (sauf si vous avez défini une casse collation ) effectuera automatiquement une correspondance insensible à la casse et utilisera ses index. ( bar =? ).
  • À partir de votre code en dehors de la base de données, conservez les champs bar et bar_lower , où bar_lower contient le résultat de inférieur (bar) . (Cela peut également être possible en utilisant des déclencheurs de base de données). (Voir une discussion de cette solution sur Drupal ). Ceci est maladroit mais fonctionne au moins de la même manière sur à peu près toutes les bases de données.
8
tims

REGEXP est insensible à la casse (sauf s'il est utilisé avec BINARY), et peut être utilisé comme tel ...

    SELECT id FROM person WHERE name REGEXP 'john';

... pour correspondre à "John", "JOHN", "john", etc.

5
Ben Wilhelm
2
RuelB

Si vous utilisez PostgreSQL 8.4, vous pouvez utiliser le module citext pour créer des champs de texte insensibles à la casse.

2
MkV

Vous pouvez également utiliser ~ * dans postgres si vous souhaitez faire correspondre une sous-chaîne dans un bloc. ~ correspond à la sous-chaîne sensible à la casse, ~ * sous-chaîne insensible à la casse. C'est une opération lente, mais pourrais-je le trouver utile pour les recherches.

Select * from table where column ~* 'UnEvEn TeXt';
Select * from table where column ~ 'Uneven text';

Les deux toucheraient "Un texte inégal ici" Seul le premier toucherait "Un texte inégal ici"

1
Sheldon Ross

Vous pouvez également envisager de vérifier le plugin searchlogic , qui fait le commutateur LIKE/ILIKE pour vous.

1
Trevor Turk

La conversion en supérieur est préférable car elle couvre la syntaxe compatible pour les 3 backends de base de données Rails les plus utilisés. PostgreSQL, MySQL et SQLite prennent tous en charge cette syntaxe. Elle présente l'inconvénient (mineur) que vous devez en majuscule votre chaîne de recherche dans votre application ou dans votre chaîne de conditions, ce qui la rend un peu plus laide, mais je pense que la compatibilité que vous gagnez en vaut la peine.

MySQL et SQLite3 ont un opérateur LIKE insensible à la casse. Seul PostgreSQL possède un opérateur LIKE sensible à la casse et un opérateur ILIKE spécifique à PostgreSQL (selon le manuel) pour les recherches non sensibles à la casse. Vous pouvez spécifier ILIKE au lieu de LIKE dans vos conditions sur l'application Rails, mais sachez que l'application cessera de fonctionner sous MySQL ou SQLite.

Une troisième option pourrait être de vérifier le moteur de base de données que vous utilisez et de modifier la chaîne de recherche en conséquence. Cela pourrait être mieux fait en piratant les adaptateurs de connexion d'ActiveRecord/monkeypatching et en faisant en sorte que l'adaptateur PostgreSQL modifie la chaîne de requête pour remplacer "LIKE" par "ILIKE" avant l'exécution de la requête. Cette solution est cependant la plus compliquée et à la lumière de moyens plus simples comme mettre les deux termes en majuscule, je pense que cela ne fait pas l'effort (bien que vous obtiendrez beaucoup de points brownie pour le faire de cette façon).

0
Roadmaster