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)

433
demandé sur Community 2009-06-12 18:08:42

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

DBFiddle Demo

17
répondu Lukasz Szozda 2018-05-06 05:06:39

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

134
répondu Jeremy Stein 2018-04-30 12:22:19

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
111
répondu Benvorth 2016-05-03 03:42:32

ma méthode de la force brute pour que cela fonctionne était juste:

  1. Vidage de la table - mysqldump -u user -p database table > dump.sql
  2. 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.
  3. 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.

101
répondu Cayetano Gonçalves 2018-04-19 22:29:04

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 ._,');
39
répondu rasika godawatte 2016-09-05 07:22:20

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

29
répondu Jay Patel 2015-07-21 21:34:57

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 .

13
répondu dotancohen 2017-05-23 12:18:23

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

Rextester Démo

Limitations

  1. 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é).
  2. 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é).
  3. 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.
  4. 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:

8
répondu Steve Chambers 2018-08-03 13:19:11

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[[:>:]]'
7
répondu Eddie B 2012-09-28 03:15:52

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');
4
répondu user3796869 2014-12-01 08:37:15