regexp用法 - 如何在MySQL中進行正則表達式替換?




mysql regexp用法 (7)

更新:現在已經成為一個博客文章: http://stevettt.blogspot.co.uk/2018/02/a-mysql-regular-expression-replace.html : http://stevettt.blogspot.co.uk/2018/02/a-mysql-regular-expression-replace.html

以下擴展了Rasika Godawatte提供功能,但是通過所有必要的子串進行拖拉 ,而不是只測試單個字符:

-- ------------------------------------------------------------------------------------
-- 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演示

限制

  1. 當主題字符串很大時,此方法當然需要一段時間。 更新:現在已經添加了最小和最大匹配長度參數,以便在已知這些參數時提高效率(零=未知/無限制)。
  2. 它不允許替換反向引用(例如\1\2等)來替換捕獲組。 如果需要此功能,請參閱此答案該答案嘗試通過更新函數以允許在每個找到的匹配中進行二級查找和替換(以增加複雜性為代價)來提供解決方法。
  3. 如果在模式中使用^和/或$ ,它們必須分別處於開始和結束位置 - 例如(^start|end$)等模式不受支持。
  4. 有一個“貪婪”標誌來指定整體匹配應該是貪婪還是非貪婪。 不支持在單個正則表達式中結合貪婪和惰性匹配(例如a.*?b.* )。

我有一個〜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 ,但那些只檢查是否有匹配,不匹配什麼

(我可以從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=...緩慢和醜陋的黑客)


你'可以'做到這一點......但這不是很明智......這與我會盡力的大膽一樣......只要完整的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[[:>:]]'

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

我們解決了這個問題,而不使用正則表達式這個查詢只替換完全匹配字符串。

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


我最近編寫了一個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 ._,');

我的蠻力方法得到這個工作只是:

  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 regeular表達式。
  3. 導入表 - mysqlimport -u user -p database table < dump.sql

如果您想確保字符串不在數據集的其他位置,請運行一些正則表達式以確保它們都出現在類似的環境中。 在運行替換之前創建備份也不難,以免意外摧毀丟失深度信息的內容。


沒有。

但是,如果您有權訪問服務器,則可以使用用戶定義的函數(UDF),如mysql-udf-regexp

編輯: MySQL 8.0+你可以使用本地REGEXP_REPLACE。 上面的答案更多





mysql-udf