当前位置:  开发笔记 > 编程语言 > 正文

如何在MySQL中替换正则表达式?

如何解决《如何在MySQL中替换正则表达式?》经验,为你挑选了10个好方法。

我有一张约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=...",但这看起来像一个最后的缓慢和丑陋的黑客)



1> Jeremy Stein..:

没有.

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

编辑: MySQL 8.0+你可以原生使用REGEXP_REPLACE.更多回答如上


不幸的是,mysql-udf-regexp似乎不支持多字节字符.regexp_replace('äöõü','ä','')返回一个长数字字符串而不是真实文本.
Windows用户:此处链接的UDF库似乎没有良好的Windows支持.概述的Windows安装方法对我来说效果不佳.
REGEXP_REPLACE作为用户定义的函数?看起来很有前途,会调查一下.谢谢!
MySQL本身不支持具有RegEx功能的多字节字符.
@lkraav你应该尝试下面的lib_mysqludf_preg库,因为它工作得很好.这是详细版本,因为它默认返回一个blob,我不知道你是否有一个多字节字符集作为默认值:select cast(TR as char)COLLATE utf8_unicode_ci from(select preg_replace('/ä/','', 'öõüä')R)T

2> Benvorth..:

请改用MariaDB.它有一个功能

REGEXP_REPLACE(col, regexp, replace)

请参阅MariaDB文档和PCRE正则表达式增强功能

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

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

回报

over - stack - flow


改变整个平台并不是一个现实的解决方案.
这是来自mariadb 10
下次我需要它,这里是更改整列的语法:`UPDATE表SET Name = REGEXP_REPLACE(Name," - 2 $","\\ 1")`这从整个abcxyz-2中删除-2立刻.
@DavidBaucum MariaDB是MySQL的直接替代品.所以这不是"平台的变化",而更像是为同一次旅行选择不同的航空公司
@Benvorth [MySQL 8.0也支持它](/sf/ask/17360801/).

3> Ryan Ward..:

我的强力方法让这个工作只是:

    转储表 - mysqldump -u user -p database table > dump.sql

    查找并替换几个模式 - find /path/to/dump.sql -type f -exec sed -i 's/old_string/new_string/g' {} \;显然,您可以在文件上执行其他perl regeular表达式.

    导入表格 - mysqlimport -u user -p database table < dump.sql

如果要确保字符串不在数据集中的其他位置,请运行一些正则表达式以确保它们都出现在类似的环境中.在运行替换之前创建备份并不困难,以防您意外破坏丢失信息深度的内容.


使用大数据集或使用参照完整性非常危险且不实用:要删除数据然后再次插入,您必须关闭参照完整性,同时也要关闭数据库.
好的,那也应该有用; 我没有考虑离线替换.那个开箱即用的好想法!
对我来说似乎很奇怪,你会使用find,我会将命令缩短为sed -i's/old_string/new_string/g'/path/to/dump.sql
过去使用过这种方法,我与劳尔合作,这是非常危险的.您还需要绝对确定,您的字符串不在数据集的任何位置.
如果替换不能打破SQL本身,可以工作.

4> Lukasz Szozd..:

MySQL 8.0+你可以原生使用REGEXP_REPLACE.

12.5.2正则表达式:

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

将字符串expr中与模式pat指定的正则表达式匹配的匹配项替换为替换字符串repl,并返回结果字符串.如果expr,pat或repl为NULL,则返回值为NULL.

和正则表达式支持:

以前,MySQL使用Henry Spencer正则表达式库来支持正则表达式运算符(REGEXP,RLIKE).

正则表达式支持已使用国际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演示



5> 小智..:

我最近编写了一个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 ._,');


我将强调上述观点:此函数替换与单字符表达式匹配的*字符*.它在上面说它用于"使用正则表达式重新生成字符串",这可能有点误导.它发挥了作用,但并不是要求的工作.(不是抱怨 - 只是为了拯救领导者走错路)
在你回答中实际包含代码而不是发布裸链接会更有帮助.
很好 - 但遗憾的是没有处理像`select regex_replace('.*(abc).*','\n','noabcde')`这样的引用(返回'noabcde',而不是'abc').

6> 小智..:

我们解决这个问题而不使用正则表达式这个查询只替换完全匹配字符串.

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

例:

emp_id employee_firstname

1杰伊

2 jay ajay

3杰伊

执行查询结果后:

emp_id employee_firstname

1 abc

2 abc ajay

3 abc


他之前和之后用空格填充了雇员.这允许他搜索 - 替换(空间)employeename(空间),如果它的一部分更大的字符串"ajay",这将避免抓住雇员名称"jay".然后他完成后修剪空间.

7> dotancohen..:

我很高兴地报告说,既然问了这个问题,现在有一个满意的答案!看看这个了不起的包:

https://github.com/mysqludf/lib_mysqludf_preg

示例SQL:

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

我发现此博客文章中的包与此问题相关联.



8> Steve Chambe..:

更新2: MySQL 8.0中现在提供了一组有用的正则表达式函数,包括REGEXP_REPLACE.除非您被限制使用早期版本,否则这会使读取变得不必要.


更新1:现在已将其变成博客文章:http://stevettt.blogspot.co.uk/2018/02/a-mysql-regular-expression-replace.html


以下内容扩展了Rasika Godawatte提供的功能,但是通过所有必要的子串搜索而不仅仅是测试单个字符:

-- ------------------------------------------------------------------------------------
-- USAGE
-- ------------------------------------------------------------------------------------
-- SELECT reg_replace(,
--                    ,
--                    ,
--                    ,
--                    ,
--                    );
-- where:
--  is the string to look in for doing the replacements
--  is the regular expression to match against
--  is the replacement string
--  is TRUE for greedy matching or FALSE for non-greedy matching
--  specifies the minimum match length
--  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演示

限制

    当主题字符串很大时,这种方法当然需要一段时间.更新:现在已添加最小和最大匹配长度参数,以便在知道这些参数时提高效率(零=未知/无限制).

    不会允许反向引用(例如取代\1,\2 等等)来替换捕获组.如果需要此功能,请参阅此答案,该答案试图通过更新函数来提供解决方法,以允许在每个找到的匹配中进行辅助查找和替换(以增加的复杂性为代价).

    如果^和/或$在模式中使用,它们必须分别位于开始和结束 - 例如,(^start|end$)不支持的模式.

    有一个"贪婪"的标志来指定整体匹配是贪婪还是非贪婪.a.*?b.*不支持在单个正则表达式(例如)中组合贪婪和惰性匹配.

用法示例

该函数已用于回答以下StackOverflow问题:

如何计算MySQL /正则表达式替换器中的单词?

如何在MySQL字符串中提取第n个单词并计算单词出现次数?

如何从MySQL中的文本字段中提取两个连续数字?

如何从MySQL中的字符串中删除所有非字母数字字符?

如何替换MySQL字符串中特定字符的每个其他实例?



9> Eddie B..:

你可以做到这一点......但这并不是很明智......这就像我会尝试的那样大胆......只要完整的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[[:>:]]'



10> user3796869..:

我们可以在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');

推荐阅读
吻过彩虹的脸_378
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有