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'
+-----------------------------+
Si replace(lower())
ne fonctionne pas, vous devrez créer une autre fonction.
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
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
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 ...
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.
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 ;
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.