MySQL替换给定域的.html链接
Posted
技术标签:
【中文标题】MySQL替换给定域的.html链接【英文标题】:MySQL replace .html links of given domain 【发布时间】:2016-11-27 06:29:24 【问题描述】:在我的数据库中有包含大量内部链接的内容字段。我必须将链接结构从 www.mydomain.de/page.html 更改为 www.mydomain.de/page/,但替换语句应该尊重域:
这是预期被替换的内容:
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” 找到的“.html”数量 = 找到的“mydomain.de”数量它不必 100% 匹配所有 'mydomain.de' 链接,我对 90% 感到满意,但外部链接中不应有错误的替换。
【问题讨论】:
如果这必须在 mysql / ansi sql 中 100% 完成,我认为它不能完成......当然,除非你现在所有可能的 url 没有查询字符串(在在这种情况下,这将是微不足道的)。但是,如果您不这样做...问题是,您可以将域(和 url)与正则表达式匹配,但既不能将其提取到某个临时变量/表/任何东西中,也不能用正则表达式替换。如果您愿意让一些外部脚本来完成这项工作,那几乎是微不足道的。如果存储过程是可能的,那么可能有一个解决方案,但它可能很难看。 “www.mydomain.de”在contentfield
中最多出现多少次?如果未知,这不能仅通过update
语句来完成。
@JPG 你可以多次运行更新吗?
@Jakumi 所以存储过程可能是一个解决方案,不是吗?
其实我从来没有写过存储过程,我只是假设它可以做到。我刚刚发现:***.com/questions/986826/…
【参考方案1】:
更新:现在已将其制成博客文章:http://stevettt.blogspot.co.uk/2018/02/a-mysql-regular-expression-replace.html
请参阅以下 Rextester Fiddle,我认为它应该会产生您所要求的所有结果:
Rextester Demo
说明
为此需要一个模式替换功能,但不幸的是MySQL doesn't provide such a thing。所以我写了一个(基于另一个不够充分的)并发布了here。如参考答案中所述,此功能有一个限制,即不允许使用反向引用替换捕获组。因此,它在小提琴中稍作调整,以采用更多参数,允许它在找到的匹配项中执行递归替换以进行替换。 (注意根据this excellent answer,在正则表达式中使用允许的 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 委托给自 only stored procedures will allow recursion in 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 ;
【讨论】:
非常感谢您的完整回答和大量解释!【参考方案2】:这是你想要的吗?
UPDATE tablename
SET contentfield = REPLACE(contentfield, '.html', '/')
WHERE contentfield like 'www.mydomain.de/%';
它应该适用于问题中的示例。
如果您愿意,您可以使用条件仅匹配其中实际包含“.html”的行。
WHERE contentfield like 'www.mydomain.de/%.html%'
【讨论】:
contentfield 可以包含更多链接 - 我添加了一个示例来提问【参考方案3】:我只是将表格导出为 CSV 或其他东西,然后使用记事本++/excel/等。内部替换工具 '.html' 和 '/'。
然后导入回SQL。
此外,由于 mysql 支持正则表达式,因此您可以搜索包含 .html 的域。
mydomain.de[^s]+.html
【讨论】:
【参考方案4】:你可以像这样使用
UPDATE tablename
SET contentfield = REPLACE(contentfield, '.html', '/')
where contentfield like 'www.mydomain.de%'
AND contentfield like '%html%'
AND ( contentfield not like 'www.another-domain.com/somelink%' OR
contentfield not like 'www.another-domain.com/subfolder/link%' )
【讨论】:
contentfield 可以包含更多链接 - 我添加了一个示例来提问 不幸的是,我知道“www.mydomain.de”是一个字符串,但“another-domain.com”是网络上的任何链接 你有一些登录信息来获取 .. "another-domain.com" 吗? ..如果是的话,可以使用这个逻辑..以上是关于MySQL替换给定域的.html链接的主要内容,如果未能解决你的问题,请参考以下文章