CẬP NHẬT 2: Một tập hợp các hàm regex hữu ích bao gồm REGEXP_REPLACE hiện đã được cung cấp trong MySQL 8.0. Điều này ám chỉ việc đọc không cần thiết trừ khi bạn bị hạn chế sử dụng phiên bản cũ hơn.
CẬP NHẬT 1: Hiện tại đã biến bài viết này thành một bài đăng trên blog: http://stevettt.blogspot.co.uk/2018/02/a-mysql-THER-expression-replace.html
Phần sau đây mở rộng dựa trên chức năng do Rasika Godawatte cung cấp nhưng truy tìm tất cả các chuỗi con cần thiết thay vì chỉ kiểm tra các ký tự đơn:
-- ------------------------------------------------------------------------------------
-- 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 ;
Bản giới thiệu
Trình diễn Rextester
Hạn chế
- Phương pháp này tất nhiên sẽ mất một lúc khi chuỗi chủ đề lớn. Cập nhật: Hiện đã thêm các tham số độ dài khớp tối thiểu và tối đa để cải thiện hiệu quả khi chúng được biết đến (zero = unknown / Unlimited).
- Nó sẽ không cho phép thay thế các phản hồi (ví dụ
\1
, \2
v.v.) để thay thế các nhóm bắt giữ. Nếu chức năng này là cần thiết, vui lòng xem câu trả lời này cố gắng cung cấp cách giải quyết bằng cách cập nhật chức năng để cho phép tìm và thay thế thứ cấp trong mỗi kết quả tìm thấy (với chi phí tăng độ phức tạp).
- Nếu
^
và / hoặc $
được sử dụng trong mẫu, chúng phải ở đầu và cuối tương ứng - ví dụ như các mẫu như (^start|end$)
không được hỗ trợ.
- Có một cờ "tham lam" để xác định xem kết hợp tổng thể nên tham lam hay không tham lam. Kết hợp kết hợp tham lam và lười biếng trong một biểu thức chính quy duy nhất (ví dụ
a.*?b.*
) không được hỗ trợ.
Ví dụ sử dụng
Hàm đã được sử dụng để trả lời các câu hỏi StackOverflow sau: