Comment faire pour remplacer une expression régulière dans MySQL?
j'ai une table avec des lignes ~500k; varchar(255) colonne UTF8 filename
contient un nom de fichier;
j'essaie d'enlever plusieurs caractères étranges du nom du fichier - j'ai pensé que j'utiliserais une classe de caractères: [^a-zA-Z0-9()_ .-]
maintenant, y a-t-il une fonction dans MySQL qui vous permet de remplacer par une expression régulière ? Je suis à la recherche d'une fonctionnalité similaire pour remplacer() la fonction-exemple simplifié suit:
SELECT REPLACE('stackowerflow', 'ower', 'over');
Output: "stackoverflow"
/* does something like this exist? */
SELECT X_REG_REPLACE('Stackoverflow','/[A-Zf]/','-');
Output: "-tackover-low"
je sais à propos de REGEXP/RLIKE , mais seulement ceux cochez la case si il y a un match, de ne pas ce le match est.
I pourrait faire " un SELECT pkey_id,filename FROM foo WHERE filename RLIKE '[^a-zA-Z0-9()_ .-]'
" à partir d'un script PHP, faire un preg_replace
et ensuite " UPDATE foo ... WHERE pkey_id=...
", mais qui ressemble à un dernier recours lent et moche hack)
10 réponses
MySQL 8.0+ vous pouvez utiliser nativement REGEXP_REPLACE
.
12.5.2 Expressions Régulières :
REGEXP_REPLACE (expr, pat, repl[, pos[, occurrence[, match_type]])
remplace les occurrences dans la chaîne expr qui correspondent à l'expression régulière spécifiée par la pat pattern avec la chaîne de remplacement repl, et renvoie la chaîne résultante. Si expr, pat, ou repl est NULL, la valeur de retour est NULL.
et support des expressions Régulières :
auparavant, MySQL utilisait la bibliothèque D'expression régulière Henry Spencer pour supporter les opérateurs d'expression régulière (REGEXP, RLIKE).
le support D'expression régulière a été réimplémenté en utilisant des composants internationaux pour Unicode (ICU), qui fournit le support Unicode complet et est multibyte sûr. La fonction REGEXP_LIKE () effectue régulièrement des appariements d'expressions à la manière des opérateurs REGEXP et RLIKE, qui sont maintenant synonymes pour cette fonction. de plus, les fonctions REGEXP_INSTR (), REGEXP_REPLACE () et REGEXP_SUBSTR() sont disponibles pour trouver les positions de correspondance et effectuer la substitution et l'extraction de substrats, respectivement.
SELECT REGEXP_REPLACE('Stackoverflow','[A-Zf]','-',1,0,'c');
-- Output:
-tackover-low
Pas de.
mais si vous avez accès à votre serveur, vous pouvez utiliser une fonction définie par l'utilisateur (UDF) comme mysql-udf-regexp .
EDIT: MySQL 8.0+ you could use natively REGEXP_REPLACE. Voir la réponse ci-dessus
utilisez MariaDB à la place. Il a une fonction
REGEXP_REPLACE(col, regexp, replace)
Voir MariaDB docs et expression Régulière PCRE améliorations
notez que vous pouvez également utiliser le groupement regexp (j'ai trouvé cela très utile):
SELECT REGEXP_REPLACE("stackoverflow", "(stack)(over)(flow)", '\2 - \1 - \3')
retourne
over - stack - flow
ma méthode de la force brute pour que cela fonctionne était juste:
- Vidage de la table -
mysqldump -u user -p database table > dump.sql
- trouver et remplacer un couple patterns -
find /path/to/dump.sql -type f -exec sed -i 's/old_string/new_string/g' {} \;
, Il ya évidemment d'autres expressions régéulaires perl que vous pourriez effectuer sur le fichier ainsi. - importer la table -
mysqlimport -u user -p database table < dump.sql
si vous voulez vous assurer que la chaîne n'est pas ailleurs dans votre ensemble de données, exécutez quelques-uns réguliers expressions pour s'assurer qu'ils apparaissent tous dans un environnement similaire. Il n'est pas non plus si difficile de créer une sauvegarde avant d'exécuter un remplacement, au cas où vous détruisez accidentellement quelque chose qui perd la profondeur de l'information.
j'ai récemment écrit une fonction MySQL pour remplacer les chaînes en utilisant des expressions régulières. Vous pouvez trouver mon message à l'endroit suivant:
http://techras.wordpress.com/2011/06/02/regex-replace-for-mysql /
voici le code de fonction:
DELIMITER $$
CREATE FUNCTION `regex_replace`(pattern VARCHAR(1000),replacement VARCHAR(1000),original VARCHAR(1000))
RETURNS VARCHAR(1000)
DETERMINISTIC
BEGIN
DECLARE temp VARCHAR(1000);
DECLARE ch VARCHAR(1);
DECLARE i INT;
SET i = 1;
SET temp = '';
IF original REGEXP pattern THEN
loop_label: LOOP
IF i>CHAR_LENGTH(original) THEN
LEAVE loop_label;
END IF;
SET ch = SUBSTRING(original,i,1);
IF NOT ch REGEXP pattern THEN
SET temp = CONCAT(temp,ch);
ELSE
SET temp = CONCAT(temp,replacement);
END IF;
SET i=i+1;
END LOOP;
ELSE
SET temp = original;
END IF;
RETURN temp;
END$$
DELIMITER ;
exemple d'exécution:
mysql> select regex_replace('[^a-zA-Z0-9\-]','','2my test3_text-to. check \ my- sql (regular) ,expressions ._,');
nous résolvons ce problème sans utiliser regex cette requête remplace uniquement la chaîne de correspondance exacte.
update employee set
employee_firstname =
trim(REPLACE(concat(" ",employee_firstname," "),' jay ',' abc '))
exemple:
emp_id employee_firstname
1 jay
2 jay ajay
3 jay
après l'exécution du résultat de la requête:
emp_id employee_firstname
1 abc
2 abc ajay
3 abc
je suis heureux d'annoncer que depuis que cette question a été posée, maintenant il ya une réponse satisfaisante! Jetez un oeil à ce paquet formidable:
https://github.com/mysqludf/lib_mysqludf_preg
échantillon SQL:
SELECT PREG_REPLACE('/(.*?)(fox)/' , 'dog' , 'the quick brown fox' ) AS demo;
j'ai trouvé le paquet de ce billet de blog comme lien sur cette question .
mise à JOUR 2: UN ensemble de fonctions regex, y compris REGEXP_REPLACE ont été fournis dans MySQL 8.0. Cela rend la lecture sur inutile sauf si vous êtes contraint d'utiliser une version antérieure.
Maj 1: en ont fait un billet de blog: http://stevettt.blogspot.co.uk/2018/02/a-mysql-regular-expression-replace.html
ce qui suit élargit la fonction fournie par Rasika Godawatte mais les chaluts à travers tous les substrats nécessaires plutôt que de simplement tester des caractères simples:
-- ------------------------------------------------------------------------------------
-- USAGE
-- ------------------------------------------------------------------------------------
-- SELECT reg_replace(<subject>,
-- <pattern>,
-- <replacement>,
-- <greedy>,
-- <minMatchLen>,
-- <maxMatchLen>);
-- where:
-- <subject> is the string to look in for doing the replacements
-- <pattern> is the regular expression to match against
-- <replacement> is the replacement string
-- <greedy> is TRUE for greedy matching or FALSE for non-greedy matching
-- <minMatchLen> specifies the minimum match length
-- <maxMatchLen> specifies the maximum match length
-- (minMatchLen and maxMatchLen are used to improve efficiency but are
-- optional and can be set to 0 or NULL if not known/required)
-- Example:
-- SELECT reg_replace(txt, '^[Tt][^ ]* ', 'a', TRUE, 2, 0) FROM tbl;
DROP FUNCTION IF EXISTS reg_replace;
DELIMITER //
CREATE FUNCTION reg_replace(subject VARCHAR(21845), pattern VARCHAR(21845),
replacement VARCHAR(21845), greedy BOOLEAN, minMatchLen INT, maxMatchLen INT)
RETURNS VARCHAR(21845) DETERMINISTIC BEGIN
DECLARE result, subStr, usePattern VARCHAR(21845);
DECLARE startPos, prevStartPos, startInc, len, lenInc INT;
IF subject REGEXP pattern THEN
SET result = '';
-- Sanitize input parameter values
SET minMatchLen = IF(minMatchLen < 1, 1, minMatchLen);
SET maxMatchLen = IF(maxMatchLen < 1 OR maxMatchLen > CHAR_LENGTH(subject),
CHAR_LENGTH(subject), maxMatchLen);
-- Set the pattern to use to match an entire string rather than part of a string
SET usePattern = IF (LEFT(pattern, 1) = '^', pattern, CONCAT('^', pattern));
SET usePattern = IF (RIGHT(pattern, 1) = '$', usePattern, CONCAT(usePattern, '$'));
-- Set start position to 1 if pattern starts with ^ or doesn't end with $.
IF LEFT(pattern, 1) = '^' OR RIGHT(pattern, 1) <> '$' THEN
SET startPos = 1, startInc = 1;
-- Otherwise (i.e. pattern ends with $ but doesn't start with ^): Set start pos
-- to the min or max match length from the end (depending on "greedy" flag).
ELSEIF greedy THEN
SET startPos = CHAR_LENGTH(subject) - maxMatchLen + 1, startInc = 1;
ELSE
SET startPos = CHAR_LENGTH(subject) - minMatchLen + 1, startInc = -1;
END IF;
WHILE startPos >= 1 AND startPos <= CHAR_LENGTH(subject)
AND startPos + minMatchLen - 1 <= CHAR_LENGTH(subject)
AND !(LEFT(pattern, 1) = '^' AND startPos <> 1)
AND !(RIGHT(pattern, 1) = '$'
AND startPos + maxMatchLen - 1 < CHAR_LENGTH(subject)) DO
-- Set start length to maximum if matching greedily or pattern ends with $.
-- Otherwise set starting length to the minimum match length.
IF greedy OR RIGHT(pattern, 1) = '$' THEN
SET len = LEAST(CHAR_LENGTH(subject) - startPos + 1, maxMatchLen), lenInc = -1;
ELSE
SET len = minMatchLen, lenInc = 1;
END IF;
SET prevStartPos = startPos;
lenLoop: WHILE len >= 1 AND len <= maxMatchLen
AND startPos + len - 1 <= CHAR_LENGTH(subject)
AND !(RIGHT(pattern, 1) = '$'
AND startPos + len - 1 <> CHAR_LENGTH(subject)) DO
SET subStr = SUBSTRING(subject, startPos, len);
IF subStr REGEXP usePattern THEN
SET result = IF(startInc = 1,
CONCAT(result, replacement), CONCAT(replacement, result));
SET startPos = startPos + startInc * len;
LEAVE lenLoop;
END IF;
SET len = len + lenInc;
END WHILE;
IF (startPos = prevStartPos) THEN
SET result = IF(startInc = 1, CONCAT(result, SUBSTRING(subject, startPos, 1)),
CONCAT(SUBSTRING(subject, startPos, 1), result));
SET startPos = startPos + startInc;
END IF;
END WHILE;
IF startInc = 1 AND startPos <= CHAR_LENGTH(subject) THEN
SET result = CONCAT(result, RIGHT(subject, CHAR_LENGTH(subject) + 1 - startPos));
ELSEIF startInc = -1 AND startPos >= 1 THEN
SET result = CONCAT(LEFT(subject, startPos), result);
END IF;
ELSE
SET result = subject;
END IF;
RETURN result;
END//
DELIMITER ;
Démo
Limitations
- Cette méthode va bien sûr prendre un certain temps lorsque le sujet la chaîne est grande. mise à jour: ont maintenant ajouté des paramètres de longueur de correspondance minimale et maximale pour améliorer l'efficacité lorsque ceux-ci sont connus (zéro = inconnu/illimité).
- Il ne sera pas autoriser la substitution de références arrières (par exemple,
,
etc.) pour remplacer les groupes de capture. Si cette fonctionnalité est nécessaire, veuillez consulter cette réponse qui tente de fournir une solution de contournement par la mise à jour de la fonction de permettre un secondaire rechercher et remplacer dans chaque trouvé match (au détriment de l'augmentation de la complexité).
- si
^
et/ou$
est utilisé dans le modèle, ils doivent être au tout début et très fin respectivement - par exemple, les modèles tels que(^start|end$)
ne sont pas pris en charge. - il y a un drapeau" greedy " pour préciser si l'ensemble l'appariement doit être gourmand ou non gourmand. La combinaison d'appariement gourmand et paresseux dans une seule expression régulière (par exemple
a.*?b.*
) n'est pas supportée.
Exemples D'Usage
la fonction a été utilisée pour répondre aux questions suivantes sur le débordement des piles:
- Comment compter le nombre de mots dans MySQL / expression régulière de la poudre?
- comment extraire le nième mot et compter les occurrences de mot dans un MySQL chaîne?
- comment extraire deux chiffres consécutifs d'un champ de texte dans MySQL?
- Comment supprimer tous les caractères numériques non alpha d'une chaîne de caractères MySQL?
- comment remplacer toutes les autres instances d'un caractère particulier dans un MySQL chaîne?
vous "pouvez" le faire ... mais ce n'est pas très sage ... c'est environ aussi audacieux que je vais essayer ... dans la mesure où le plein RegEx prennent en charge votre beaucoup mieux à l'aide de perl ou similaire.
UPDATE db.tbl
SET column =
CASE
WHEN column REGEXP '[[:<:]]WORD_TO_REPLACE[[:>:]]'
THEN REPLACE(column,'WORD_TO_REPLACE','REPLACEMENT')
END
WHERE column REGEXP '[[:<:]]WORD_TO_REPLACE[[:>:]]'
nous pouvons utiliser si la condition dans SELECT query comme ci-dessous:
supposons que pour quelque chose avec "ABC", "ABC1","ABC2", "ABC3",..., nous voulons remplacer par "ABC" puis en utilisant REGEXP et si la condition() dans la requête SELECT, nous pouvons y arriver.
syntaxe:
SELECT IF(column_name REGEXP 'ABC[0-9]$','ABC',column_name)
FROM table1
WHERE column_name LIKE 'ABC%';
exemple:
SELECT IF('ABC1' REGEXP 'ABC[0-9]$','ABC','ABC1');