Как сделать регулярное выражение заменить в MySQL? - PullRequest
467 голосов
/ 12 июня 2009

У меня есть таблица с ~ 500k строк; varchar (255) UTF8 столбец filename содержит имя файла;

Я пытаюсь вырезать из имени файла разные странные символы - подумал, что буду использовать класс символов: [^a-zA-Z0-9()_ .\-]

Теперь, есть ли в MySQL функция, которая позволяет заменять регулярное выражение ? Я ищу функциональность, аналогичную функции REPLACE () - приведен упрощенный пример:

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

Output: "stackoverflow"

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

Output: "-tackover-low"

Я знаю о REGEXP / RLIKE , но они проверяют только , если есть совпадение, а не что совпадение.

мог бы сделать "SELECT pkey_id,filename FROM foo WHERE filename RLIKE '[^a-zA-Z0-9()_ .\-]'" из скрипта PHP, сделать preg_replace, а затем "UPDATE foo ... WHERE pkey_id=...", но это выглядит как медленный и ужасный хак в крайнем случае)

Ответы [ 11 ]

140 голосов
/ 12 июня 2009

номер

Но если у вас есть доступ к вашему серверу, вы можете использовать пользовательскую функцию (UDF), такую ​​как mysql-udf-regexp .

РЕДАКТИРОВАТЬ: MySQL 8.0+ вы можете использовать изначально REGEXP_REPLACE. Больше в ответе выше

119 голосов
/ 03 октября 2014

Используйте MariaDB вместо. Имеет функцию

REGEXP_REPLACE(col, regexp, replace)

См. MariaDB документы и PCRE Улучшения регулярных выражений

Обратите внимание, что вы также можете использовать группировку регулярных выражений (я нашел это очень полезным):

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

возвращает

over - stack - flow
106 голосов
/ 26 февраля 2012

Мой метод грубой силы, чтобы заставить это работать, был просто:

  1. Дамп таблицы - mysqldump -u user -p database table > dump.sql
  2. Найти и заменить пару шаблонов - find /path/to/dump.sql -type f -exec sed -i 's/old_string/new_string/g' {} \;, Очевидно, есть и другие регулярные выражения perl, которые вы также можете выполнить с файлом.
  3. Импорт таблицы - mysqlimport -u user -p database table < dump.sql

Если вы хотите убедиться, что строки нет в другом месте в вашем наборе данных, запустите несколько регулярных выражений, чтобы убедиться, что все они встречаются в похожей среде. Также не сложно создать резервную копию перед выполнением замены на случай, если вы случайно уничтожите что-то, что приведет к потере информации.

42 голосов
/ 19 апреля 2018

MySQL 8.0+, вы можете использовать нативно REGEXP_REPLACE.

12.5.2 Регулярные выражения :

REGEXP_REPLACE (expr, pat, repl [, pos [, вхождение [, match_type]]])

Заменяет вхождения в строке expr, которые соответствуют регулярному выражению, указанному в шаблоне pat, на строку замены repl и возвращает результирующую строку. Если expr, pat или repl имеют значение NULL, возвращаемое значение равно NULL.

и Поддержка регулярных выражений :

Ранее MySQL использовал библиотеку регулярных выражений Генри Спенсера для поддержки операторов регулярных выражений (REGEXP, RLIKE).

Поддержка регулярных выражений была переопределена с использованием International Components for Unicode (ICU), которая обеспечивает полную поддержку Unicode и является многобайтовой безопасностью. Функция REGEXP_LIKE () выполняет сопоставление регулярных выражений в виде операторов REGEXP и RLIKE, которые теперь являются синонимами для этой функции. Кроме того, функции REGEXP_INSTR (), REGEXP_REPLACE () и REGEXP_SUBSTR () доступны для поиска позиций совпадения и выполнения подстановки и извлечения подстроки соответственно.

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

DBFiddle Demo

42 голосов
/ 02 июня 2011

Я недавно написал функцию MySQL для замены строк с помощью регулярных выражений. Вы можете найти мой пост в следующем месте:

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

Вот код функции:

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 ;

Пример выполнения:

mysql> select regex_replace('[^a-zA-Z0-9\-]','','2my test3_text-to. check \\ my- sql (regular) ,expressions ._,');
34 голосов
/ 19 декабря 2014

мы решаем эту проблему без использования регулярных выражений этот запрос заменяет только строку с точным соответствием.

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

Пример:

emp_id имя_пользователя

1 сойка

2 Jay AJAY

3 сойки

После выполнения запроса:

emp_id employee_firstname

1 азбука

2 abc ajay

3 азс

14 голосов
/ 13 ноября 2013

Я рад сообщить, что, поскольку этот вопрос был задан, теперь есть удовлетворительный ответ! Взгляните на этот потрясающий пакет:

https://github.com/mysqludf/lib_mysqludf_preg

Пример SQL:

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

Я нашел пакет от этого поста в блоге , связанный с этим вопросом .

11 голосов
/ 29 июля 2016

ОБНОВЛЕНИЕ 2: Полезный набор функций регулярных выражений, включая REGEXP_REPLACE , теперь представлен в MySQL 8.0. Это делает чтение ненужным, если вы не ограничены использованием более ранней версии.


ОБНОВЛЕНИЕ 1: Теперь превратили это в сообщение в блоге: http://stevettt.blogspot.co.uk/2018/02/a-mysql-regular-expression-replace.html


Ниже приведено описание функции , предоставленной Расикой Годаватте , но она перебирает все необходимые подстроки, а не просто проверяет отдельные символы:

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

Демо

Rextester Demo

Ограничения

  1. Этот метод, конечно, займет некоторое время, когда субъект Строка большая. Обновление: Теперь добавлены параметры минимальной и максимальной длины совпадения для повышения эффективности, когда они известны (ноль = неизвестно / не ограничено).
  2. Это не разрешает замену обратных ссылок (например, \1, \2 и т.д.) заменить группы захвата. Если эта функциональность необходима, см. этот ответ , в котором предпринята попытка найти обходной путь путем обновления функции, позволяющей выполнять вторичный поиск и замену в каждом найденном совпадении (за счет повышения сложности).
  3. Если в шаблоне используются ^ и / или $, они должны быть в самом начале и в самом конце соответственно - например, шаблоны, такие как (^start|end$), не поддерживаются.
  4. Существует флаг "жадный", чтобы указать, должно ли полное соответствие быть жадным или не жадным. Объединение жадного и ленивого сопоставления в одном регулярном выражении (например, a.*?b.*) не поддерживается.

Примеры использования

Эта функция используется для ответа на следующие вопросы StackOverflow:

7 голосов
/ 28 сентября 2012

Вы можете "сделать" это ... но это не очень мудро ... это настолько смело, насколько я постараюсь ... насколько полный RegEx поддерживает вашу гораздо лучшую работу с использованием perl или чего-то подобного.

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[[:>:]]'
5 голосов
/ 01 декабря 2014

Мы можем использовать условие IF в запросе SELECT, как показано ниже:

Предположим, что для чего-либо с "ABC", "ABC1", "ABC2", "ABC3", ... мы хотим заменить на "ABC", а затем с помощью условий REGEXP и IF () в запросе SELECT, мы можно достичь этого.

Синтаксис:

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

Пример:

SELECT IF('ABC1' REGEXP 'ABC[0-9]$','ABC','ABC1');
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...