อัปเดต 2:ชุดฟังก์ชัน regex ที่มีประโยชน์ซึ่งรวมถึงREGEXP_REPLACEได้รับการจัดเตรียมไว้แล้วใน MySQL 8.0 การแสดงผลนี้เป็นการอ่านที่ไม่จำเป็นเว้นแต่คุณจะถูกบังคับให้ใช้เวอร์ชันก่อนหน้า
อัปเดต 1:ทำให้สิ่งนี้กลายเป็นโพสต์บล็อก: 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
ข้อ จำกัด
- วิธีนี้แน่นอนว่าจะใช้เวลาสักครู่เมื่อสตริงหัวเรื่องมีขนาดใหญ่ อัปเดต:ตอนนี้ได้เพิ่มพารามิเตอร์ความยาวการจับคู่ขั้นต่ำและสูงสุดเพื่อปรับปรุงประสิทธิภาพเมื่อทราบแล้ว (ศูนย์ = ไม่ทราบ / ไม่ จำกัด )
- มันจะไม่อนุญาตให้มีการเปลี่ยนตัวของ backreferences (เช่น
\1
, \2
ฯลฯ ) เพื่อแทนที่กลุ่มจับ หากจำเป็นต้องใช้ฟังก์ชั่นนี้โปรดดูคำตอบที่พยายามให้วิธีแก้ปัญหาโดยการปรับปรุงฟังก์ชั่นเพื่อให้การค้นหารองและแทนที่ภายในการแข่งขันที่พบแต่ละครั้ง (ค่าใช้จ่ายของความซับซ้อนที่เพิ่มขึ้น)
- หาก
^
และ / หรือ$
ใช้ในรูปแบบจะต้องอยู่ที่จุดเริ่มต้นและจุดสิ้นสุดตามลำดับ - เช่นรูปแบบเช่น(^start|end$)
ไม่ได้รับการสนับสนุน
- มีการตั้งค่าสถานะ "โลภ" เพื่อระบุว่าการจับคู่โดยรวมควรเป็นโลภหรือไม่โลภ ไม่สนับสนุนการรวมการจับคู่โลภและขี้เกียจภายในนิพจน์ทั่วไป (เช่น
a.*?b.*
)
ตัวอย่างการใช้งาน
ฟังก์ชันนี้ถูกใช้เพื่อตอบคำถาม StackOverflow ต่อไปนี้: