web-dev-qa-db-fra.com

REMPLACER insensible à la casse dans MySQL?

MySQL exécute à peu près toutes les comparaisons de chaînes sous le classement par défaut ... sauf la commande REPLACE. J'ai un classement qui ne respecte pas la casse et j'ai besoin d'exécuter une REPLACE insensible à la casse. Existe-t-il un moyen de forcer REPLACE à utiliser le classement actuel plutôt que de toujours effectuer des comparaisons sensibles à la casse? Je suis disposé à mettre à niveau MySQL (actuellement en cours d'exécution 5.1) pour bénéficier de fonctionnalités supplémentaires ...

mysql> charset utf8 collation utf8_unicode_ci;
Charset changed

mysql> select 'abc' like '%B%';
+------------------+
| 'abc' like '%B%' |
+------------------+
|                1 |
+------------------+

mysql> select replace('aAbBcC', 'a', 'f');
+-----------------------------+
| replace('aAbBcC', 'a', 'f') |
+-----------------------------+
| fAbBcC                      |   <--- *NOT* 'ffbBcC'
+-----------------------------+
25
dkarp

Si replace(lower()) ne fonctionne pas, vous devrez créer une autre fonction.

18
fvox

Mes 2 centimes.

Étant donné que de nombreuses personnes ont mis à niveau MySQL vers MariaDB, elles disposeront d'une nouvelle fonction appelée REGEXP_REPLACE. Utilisez-le comme un remplacement normal, mais le motif est une expression régulière.

Ceci est un exemple de travail:

UPDATE `myTable`
SET `myField` = REGEXP_REPLACE(`myField`, '(?i)my insensitive string', 'new string') 
WHERE `myField` REGEXP '(?i)my insensitive string'

L'option (?i) rend toutes les correspondances ultérieures insensibles à la casse (si elles sont placées au début du motif comme je le fais alors tout est insensible).

Voir ici pour plus d'informations: https://mariadb.com/kb/en/mariadb/pcre/

Edit: depuis MySQL 8.0, vous pouvez aussi utiliser la fonction regexp_replace, voir la documentation: https://dev.mysql.com/doc/refman/8.0/en/regexp.html

6
santiago arizti

Fonction alternative pour une personne parlée par fvox.

DELIMITER |
CREATE FUNCTION case_insensitive_replace ( REPLACE_WHERE text, REPLACE_THIS text, REPLACE_WITH text )
RETURNS text
DETERMINISTIC 
BEGIN
    DECLARE last_occurency int DEFAULT '1';

    IF LCASE(REPLACE_THIS) = LCASE(REPLACE_WITH) OR LENGTH(REPLACE_THIS) < 1 THEN
         RETURN REPLACE_WHERE;
    END IF;

    WHILE Locate( LCASE(REPLACE_THIS), LCASE(REPLACE_WHERE), last_occurency ) > 0  DO
      BEGIN
        SET last_occurency = Locate(LCASE(REPLACE_THIS), LCASE(REPLACE_WHERE));
         SET REPLACE_WHERE = Insert( REPLACE_WHERE, last_occurency, LENGTH(REPLACE_THIS), REPLACE_WITH);
         SET last_occurency = last_occurency + LENGTH(REPLACE_WITH);
      END;
    END WHILE;
    RETURN REPLACE_WHERE;
END;
|
DELIMITER ;

Petit test:

SET @str = BINARY 'New York';
SELECT case_insensitive_replace(@str, 'y', 'K');

Réponses: New Kork

4
Aivar

Dans le cas de caractères spéciaux, il existe un comportement inattendu:

SELECT case_insensitive_replace('A', 'Ã', 'a')

Donne

a

Ce qui est inattendu ... puisque nous voulons seulement remplacer le à not A

Ce qui est encore plus bizarre:

SELECT LOCATE('Ã', 'A');

donne

0

Quel est le résultat correct ... semble avoir à faire avec l'encodage des paramètres de la procédure stockée ...

0
iwritecode

J'y suis allé avec http://pento.net/2009/02/15/case-insensitive-replace-for-mysql/ (dans la réponse de fvox) qui effectue la recherche sans distinction de casse avec un remplacement sensible à la casse et sans changer le cas de ce qui devrait être des caractères non affectés ailleurs dans la chaîne recherchée.

N.B. le commentaire plus bas sur cette même page indiquant que CHAR (255) devrait être remplacé par VARCHAR (255) - cela semblait être nécessaire pour moi aussi.

0
Wandering Zombie

Cette modification de la réponse de Luist permet de remplacer l'aiguille par une version de l'aiguille gainée différemment (deux lignes changent).

DELIMITER |
CREATE FUNCTION case_insensitive_replace ( REPLACE_WHERE text, REPLACE_THIS text, REPLACE_WITH text )
RETURNS text
DETERMINISTIC 
BEGIN
  DECLARE last_occurency int DEFAULT '1';

  IF LENGTH(REPLACE_THIS) < 1 THEN
    RETURN REPLACE_WHERE;
  END IF;

  WHILE Locate( LCASE(REPLACE_THIS), LCASE(REPLACE_WHERE), last_occurency ) > 0  DO
    BEGIN
      SET last_occurency = Locate(LCASE(REPLACE_THIS), LCASE(REPLACE_WHERE), last_occurency);
      SET REPLACE_WHERE = Insert( REPLACE_WHERE, last_occurency, LENGTH(REPLACE_THIS), REPLACE_WITH);
       SET last_occurency = last_occurency + LENGTH(REPLACE_WITH);
    END;
  END WHILE;
  RETURN REPLACE_WHERE;
END;
|
DELIMITER ;
0
notalbert

Dans les réponses précédentes, ainsi que sur le lien pento.net, les arguments de LOCATE() sont en minuscule.

Ceci est un gaspillage de ressources, car LOCATE est insensible à la casse par défaut:

mysql> select locate('el', 'HELLo');
+-----------------------+
| locate('el', 'HELLo') |
+-----------------------+
|                     2 |
+-----------------------+

Vous pouvez remplacer

WHILE Locate( LCASE(REPLACE_THIS), LCASE(REPLACE_WHERE), last_occurency ) > 0 DO

avec

WHILE Locate(REPLACE_THIS, REPLACE_WHERE, last_occurency ) > 0 DO

etc.

0
fisharebest