¿Cómo reemplazar una expresión regular en MySQL?


Tengo una tabla con ~500k filas; varchar (255) UTF8 columna filename contiene un nombre de archivo;

Estoy tratando de eliminar varios caracteres extraños del nombre del archivo-pensé que usaría una clase de caracteres: [^a-zA-Z0-9()_ .\-]

Ahora, ¿hay una función en MySQL que le permita reemplazar a través de una expresión regular? Estoy buscando una funcionalidad similar para REEMPLAZAR () función-ejemplo simplificado sigue:

SELECT REPLACE('stackowerflow', 'ower', 'over');

Output: "stackoverflow"

/* does something like this exist? */
SELECT X_REG_REPLACE('Stackoverflow','/[A-Zf]/','-'); 

Output: "-tackover-low"

Sé acerca de REGEXP / RLIKE , pero esos solo comprueban si hay una coincidencia, no qué es la coincidencia.

(Yo podría hacer un "SELECT pkey_id,filename FROM foo WHERE filename RLIKE '[^a-zA-Z0-9()_ .\-]' "desde un script PHP, hacer un preg_replace y luego" UPDATE foo ... WHERE pkey_id=...", pero eso parece un truco lento y feo de último recurso)

Author: Community, 2009-06-12

10 answers

MySQL 8.0+ se puede usar de forma nativa REGEXP_REPLACE.

12.5.2 Expresiones regulares :

REGEXP_REPLACE (expr, pat, repl [, pos [, occurrence [, match_type]]])

Reemplaza las ocurrencias en la cadena expr que coinciden con la expresión regular especificada por el patrón pat con la cadena de reemplazo repl, y devuelve la cadena resultante. Si expr, pat o repl es NULL, el valor devuelto es NULL.

Y Expresión regular soporte :

Anteriormente, MySQL usaba la biblioteca de expresiones regulares Henry Spencer para soportar operadores de expresiones regulares (REGEXP, RLIKE).

El soporte de expresiones regulares se ha reimplementado utilizando Componentes Internacionales para Unicode (ICU), que proporciona soporte Unicode completo y es multibyte seguro. La función REGEXP_LIKE () realiza la coincidencia de expresiones regulares de la manera de los operadores REGEXP y RLIKE, que ahora son sinónimos para esa función. En además, las funciones REGEXP_INSTR (), REGEXP_REPLACE () y REGEXP_SUBSTR() están disponibles para encontrar posiciones coincidentes y realizar substring substring y extracción, respectivamente.

SELECT REGEXP_REPLACE('Stackoverflow','[A-Zf]','-',1,0,'c'); 
-- Output:
-tackover-low

Demostración de DBFiddle

 17
Author: Lukasz Szozda,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-05-06 05:06:39

No.

Pero si tiene acceso a su servidor, podría usar una función definida por el usuario (UDF) como mysql-udf-regexp.

EDITAR: MySQL 8.0+ se puede utilizar de forma nativa REGEXP_REPLACE. Más en la respuesta anterior

 134
Author: Jeremy Stein,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-04-30 12:22:19

Usa MariaDB en su lugar. Tiene una función

REGEXP_REPLACE(col, regexp, replace)

Ver MariaDB docs y Mejoras de expresión regular PCRE

Tenga en cuenta que también puede usar el agrupamiento de expresiones regulares (me pareció muy útil):

SELECT REGEXP_REPLACE("stackoverflow", "(stack)(over)(flow)", '\\2 - \\1 - \\3')

Devuelve

over - stack - flow
 111
Author: Benvorth,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-05-03 03:42:32

Mi método de fuerza bruta para hacer que esto funcione fue:

  1. Volcar la tabla - mysqldump -u user -p database table > dump.sql
  2. Busque y reemplace un par de patrones - find /path/to/dump.sql -type f -exec sed -i 's/old_string/new_string/g' {} \;, obviamente hay otras expresiones regeular de perl que también podría realizar en el archivo.
  3. Importa la tabla - mysqlimport -u user -p database table < dump.sql

Si desea asegurarse de que la cadena no esté en otro lugar de su conjunto de datos, ejecute algunas expresiones regulares para asegurarse de que todas ocurran en un entorno similar. Tampoco es tan difícil crear una copia de seguridad antes de ejecutar un reemplazo, en caso de que accidentalmente destruya algo que pierda profundidad de información.

 101
Author: Cayetano Gonçalves,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-04-19 22:29:04

Recientemente escribí una función MySQL para reemplazar cadenas usando expresiones regulares. Puedes encontrar mi publicación en la siguiente ubicación:

Http://techras.wordpress.com/2011/06/02/regex-replace-for-mysql /

Aquí está el código de función:

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 ;

Ejemplo de ejecución:

mysql> select regex_replace('[^a-zA-Z0-9\-]','','2my test3_text-to. check \\ my- sql (regular) ,expressions ._,');
 39
Author: rasika godawatte,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-09-05 07:22:20

Resolvemos este problema sin usar expresiones regulares esta consulta reemplaza solo la cadena de coincidencia exacta.

update employee set
employee_firstname = 
trim(REPLACE(concat(" ",employee_firstname," "),' jay ',' abc '))

Ejemplo:

Emp_id employee_firstname

1 jay

2 jay ajay

3 jay

Después de ejecutar el resultado de la consulta:

Emp_id employee_firstname

1 abc

2 abc ajay

3 abc

 29
Author: Jay Patel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-07-21 21:34:57

Me complace informar que desde que se hizo esta pregunta, ¡ahora hay una respuesta satisfactoria! Echa un vistazo a este fantástico paquete:

Https://github.com/mysqludf/lib_mysqludf_preg

SQL de ejemplo:

SELECT PREG_REPLACE('/(.*?)(fox)/' , 'dog' , 'the quick brown fox' ) AS demo;

Encontré el paquete de esta entrada de blogcomo enlazado en esta pregunta.

 13
Author: dotancohen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:18:23

ACTUALIZACIÓN 2: Un útil conjunto de funciones regex incluyendo REGEXP_REPLACE ahora se han proporcionado en MySQL 8.0. Esto hace que la lectura sea innecesaria a menos que esté limitado a usar una versión anterior.


ACTUALIZACIÓN 1: Ahora han convertido esto en una publicación de blog: http://stevettt.blogspot.co.uk/2018/02/a-mysql-regular-expression-replace.html


Lo siguiente amplía la función proporcionada por Rasika Godawatte pero arrastra a través de todas las subcadenas necesarias en lugar de solo probar caracteres individuales:

-- ------------------------------------------------------------------------------------
-- 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 ;

Demo

Rextester Demo

Limitaciones

  1. Este método, por supuesto, va a tomar un tiempo cuando el sujeto la cuerda es grande. Actualizar: Ahora se han añadido parámetros de longitud de coincidencia mínima y máxima para mejorar la eficiencia cuando se conocen (cero = desconocido / ilimitado).
  2. no permitirá sustitución de retroreferencias (por ejemplo, \1, \2 sucesivamente.) para reemplazar los grupos de captura. Si esta funcionalidad es necesaria, consulte esta respuesta que intenta proporcionar una solución alternativa actualizando la función para permitir una búsqueda secundaria y reemplazar dentro de cada coincidencia encontrada (a expensas de una mayor complejidad).
  3. Si se usa ^y/o $ en el patrón, deben estar al principio y al final respectivamente, por ejemplo, patrones como (^start|end$) no son apoyar.
  4. Hay una bandera "greedy" para especificar si la coincidencia general debe ser greedy o no greedy. No se admite la combinación de coincidencias codiciosas y perezosas dentro de una sola expresión regular (por ejemplo, a.*?b.*).

Ejemplos De Uso

La función se ha utilizado para responder a las siguientes preguntas de StackOverflow:

 8
Author: Steve Chambers,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-08-03 13:19:11

Usted 'puede' hacerlo ... pero no es muy sabio ... esto es tan atrevido como lo intentaré ... en cuanto al soporte RegEx completo, es mucho mejor usar perl o similares.

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
Author: Eddie B,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-09-28 03:15:52

Podemos usar la condición IF en la consulta SELECT de la siguiente manera:

Supongamos que para cualquier cosa con "ABC", "ABC1", "ABC2", "ABC3",..., queremos reemplazar con " ABC " a continuación, utilizando REGEXP y IF () condición en la consulta SELECT, podemos lograr esto.

Sintaxis:

SELECT IF(column_name REGEXP 'ABC[0-9]$','ABC',column_name)
FROM table1 
WHERE column_name LIKE 'ABC%';

Ejemplo:

SELECT IF('ABC1' REGEXP 'ABC[0-9]$','ABC','ABC1');
 4
Author: user3796869,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-12-01 08:37:15