在我的数据库中,contenfields具有许多内部链接。我必须将链接结构从www.mydomain.de/page.html更改为www.mydomain.de/page/,但是replace语句应尊重该域:
这是预期将被替换的内容:
www.mydomain.de/somepage.html -> www.mydomain.de/page/ www.mydomain.de/subfolder/page.html -> www.mydomain.de/subfolder/page/ www.mydomain.de/link.html?param=1 -> www.mydomain.de/page/?param=1 www.mydomain.de/another-link.html#hash -> www.mydomain.de/page/#hash
所有其他链接都应保持不变,此处提供一些示例,但也可以是网络上的任何链接:
www.some-domain.de/link.html www.another-domain.com/somelink.html
一个内容字段中可以有不同的链接:
<p>If you want to read more, click <a href="http://www.mydomain.de/page.html">here</a> or there <a href="http://www.another-domain.com/somelink.html">there</a>
这是在做替换:
UPDATE tablename SET contentfield = REPLACE(contentfield, '.html', '/')
我的想法(但不知道如何为它们创建语句):
不一定要100%匹配所有“ mydomain.de”链接,我对90%的比例感到满意,但外部链接中应该没有错误的替换。
更新: 现在已将其发布到博客文章中:http : //stevettt.blogspot.co.uk/2018/02/a-mysql- regular-expression- replace.html
请查看以下Rextester Fiddle,我认为它应该产生您要求的所有结果:
Rextester演示
解释
为此,需要使用模式替换功能,但是不幸的是,MySQL没有提供这种功能。因此,我写了一个(基于另一个还不够),并将其张贴在这里。如参考答案中所述,此功能的局限性在于不允许使用反向引用替换捕获组。因此,它已经在小提琴中稍作调整,以采用其他参数,使其能够在找到的替换匹配项中执行递归替换。(请注意,根据此出色的答案,在正则表达式中使用了允许的URL路径字符)。
更新SQL
以下SQL将使用以下函数更新表数据:
UPDATE urls SET url = reg_replace( url, 'www\\.mydomain\\.de/[-A-Za-z0-9\\._~!\\$&''\\(\\)\\*\\+,;=:@%/]+\\.html', '/[^/]+\\.html', '/page/', TRUE, 22, -- Min match length = www.mydomain.de/?.html = 22 0, -- No max match length 7, -- Min sub-match length = /?.html = 7 0 -- No max sub-match length );
功能码
演示中使用的UDF代码也发布在下面。注意:UDF委托给存储过程,因为只有存储过程才允许在MySQL中进行递归。
-- ------------------------------------------------------------------------------------ -- USAGE -- ------------------------------------------------------------------------------------ -- SELECT reg_replace(<subject>, -- <pattern>, -- <subpattern>, -- <replacement>, -- <greedy>, -- <minMatchLen>, -- <maxMatchLen>, -- <minSubMatchLen>, -- <maxSubMatchLen>); -- where: -- <subject> is the string to look in for doing the replacements -- <pattern> is the regular expression to match against -- <subpattern> is a regular expression to match against within each -- portion of text that matches <pattern> -- <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 -- <minSubMatchLen> specifies the minimum match length -- <maxSubMatchLen> specifies the maximum match length -- (minMatchLen, maxMatchLen, minSubMatchLen and maxSubMatchLen 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, '[A-Z0-9]{3}', '[0-9]', '_', TRUE, 3, 3, 1, 1) FROM tbl; DROP FUNCTION IF EXISTS reg_replace; DELIMITER // CREATE FUNCTION reg_replace(subject VARCHAR(21845), pattern VARCHAR(21845), subpattern VARCHAR(21845), replacement VARCHAR(21845), greedy BOOLEAN, minMatchLen INT, maxMatchLen INT, minSubMatchLen INT, maxSubMatchLen INT) RETURNS VARCHAR(21845) DETERMINISTIC BEGIN DECLARE result VARCHAR(21845); CALL reg_replace_worker( subject, pattern, subpattern, replacement, greedy, minMatchLen, maxMatchLen, minSubMatchLen, maxSubMatchLen, result); RETURN result; END;// DELIMITER ; DROP PROCEDURE IF EXISTS reg_replace_worker; DELIMITER // CREATE PROCEDURE reg_replace_worker(subject VARCHAR(21845), pattern VARCHAR(21845), subpattern VARCHAR(21845), replacement VARCHAR(21845), greedy BOOLEAN, minMatchLen INT, maxMatchLen INT, minSubMatchLen INT, maxSubMatchLen INT, OUT result VARCHAR(21845)) BEGIN DECLARE subStr, usePattern, useRepl VARCHAR(21845); DECLARE startPos, prevStartPos, startInc, len, lenInc INT; SET @@SESSION.max_sp_recursion_depth = 2; 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 IF subpattern IS NULL THEN SET useRepl = replacement; ELSE CALL reg_replace_worker(subStr, subpattern, NULL, replacement, greedy, minSubMatchLen, maxSubMatchLen, NULL, NULL, useRepl); END IF; SET result = IF(startInc = 1, CONCAT(result, useRepl), CONCAT(useRepl, 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; END;// DELIMITER ;