如何在MySQL中进行正则expression式replace?

我有一个约500k行的桌子; varchar(255)UTF8列的filename包含一个文件名;

我试图从文件名中去除各种奇怪的字符 – 以为我会用字符类: [^a-zA-Z0-9()_ .\-]

现在, MySQL中有一个函数可以让你通过正则expression式来replace吗? 我正在寻找与REPLACE()函数类似的function – 简单的例子如下:

 SELECT REPLACE('stackowerflow', 'ower', 'over'); Output: "stackoverflow" /* does something like this exist? */ SELECT X_REG_REPLACE('Stackoverflow','/[A-Zf]/','-'); Output: "-tackover-low" 

我知道REGEXP / RLIKE ,但那些只检查是否有匹配,不匹配什么

(我可以从一个PHP脚本做一个“ SELECT pkey_id,filename FROM foo WHERE filename RLIKE '[^a-zA-Z0-9()_ .\-]' ”,做一个preg_replace然后“ UPDATE foo ... WHERE pkey_id=... “,但看起来像是最后一UPDATE foo ... WHERE pkey_id=...缓慢和丑陋的黑客)

没有。

但是,如果您有权访问服务器,则可以使用用户定义的函数(UDF),如mysql-udf-regexp 。

使用MariaDB来代替。 它有一个function

 REGEXP_REPLACE(col, regexp, replace) 

请参阅MariaDB文档和PCRE正则expression式增强

请注意,您也可以使用正则expression式分组(我发现这非常有用):

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

回报

 over - stack - flow 

我的蛮力方法得到这个工作只是:

  1. 转储表 – mysqldump -u user -p database table > dump.sql
  2. 查找并replace一对模式 – find /path/to/dump.sql -type f -exec sed -i 's/old_string/new_string/g' {} \; ,显然你也可以在文件上执行其他perlexpression式。
  3. 导入表 – mysqlimport -u user -p database table < dump.sql

我最近编写了一个MySQL函数来使用正则expression式replacestring。 您可以在以下位置find我的post:

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

这里是function代码:

 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 ._,'); 

我们解决这个问题,而不使用正则expression式这个查询只replace完全匹配的string。

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

例:

emp_id employee_firstname

1杰伊

2杰伊ajay

3杰

执行查询结果后:

emp_id employee_firstname

1 abc

2 abc ajay

3 abc

我很高兴地回答,自从这个问题被问到,现在有一个令人满意的答案! 看看这个了不起的包:

https://github.com/mysqludf/lib_mysqludf_preg

示例SQL:

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

我发现这个博客文章中的软件包是关于这个问题的链接。

你可以做到这一点,但这不是很明智…这是大胆的,我会尽量…只要完整的正则expression式支持使用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[[:>:]]' 

我们可以在SELECT查询中使用IF条件如下:

假设对于任何具有“ABC”,“ABC1”,“ABC2”,“ABC3”,…的任何东西,我们想用“ABC”代替,然后在SELECT查询中使用REGEXP和IF 。

句法:

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

以下扩展了Rasika Godawatte提供的function,但通过所有必要的子string进行search,而不是只testing单个字符:

 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 (ie pattern ends with $ but doesn't start with ^): Set start position -- 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// 

演示

SQL小提琴演示或Rextester演示

限制

  1. 当主题string很大时,这个方法当然需要一段时间。 更新:现在已经添加了最小和最大匹配长度参数,以提高已知的效率(零=未知/无限)。
  2. 它将不允许replace反向引用(例如\1\2等)来replace捕获组。 如果需要此function,请参阅此答案 ,尝试通过更新函数来提供解决方法,以允许在每个find的匹配中进行二级查找和replace(以增加复杂性为代价)。
  3. 如果在模式中使用^和/或$ ,它们必须分别在开始和结束处,例如(^start|end$)等模式不被支持。
  4. 有一个“贪婪的”标志来指定整体匹配应该是贪婪还是不贪婪。 不支持在单个正则expression式(例如a.*?b.* )中结合贪婪和惰性匹配。