[英]Case-insensitive REPLACE in MySQL?
MySQL runs pretty much all string comparisons under the default collation... except the REPLACE
command. MySQL 在默认排序规则下运行几乎所有字符串比较......除了
REPLACE
命令。 I have a case-insensitive collation and need to run a case-insensitive REPLACE
.我有一个不区分大小写的排序规则,需要运行一个不区分大小写的
REPLACE
。 Is there any way to force REPLACE
to use the current collation rather than always doing case-sensitive comparisons?有没有办法强制
REPLACE
使用当前排序规则而不是总是进行区分大小写的比较? I'm willing to upgrade my MySQL (currently running 5.1) to get added functionality...我愿意升级我的 MySQL(当前运行 5.1)以获得更多功能......
mysql> charset utf8 collation utf8_unicode_ci;
Charset changed
mysql> select 'abc' like '%B%';
+------------------+
| 'abc' like '%B%' |
+------------------+
| 1 |
+------------------+
mysql> select replace('aAbBcC', 'a', 'f');
+-----------------------------+
| replace('aAbBcC', 'a', 'f') |
+-----------------------------+
| fAbBcC | <--- *NOT* 'ffbBcC'
+-----------------------------+
如果replace(lower())
不起作用,则需要创建另一个函数。
My 2 cents.我的 2 美分。
Since many people have upgraded from MySQL to MariaDB those people will have available a new function called REGEXP_REPLACE
.由于许多人已从 MySQL 升级到 MariaDB,这些人将拥有一个名为
REGEXP_REPLACE
的新函数。 Use it as you would a normal replace, but the pattern is a regular expression.像正常替换一样使用它,但模式是一个正则表达式。
This is a working example:这是一个工作示例:
UPDATE `myTable`
SET `myField` = REGEXP_REPLACE(`myField`, '(?i)my insensitive string', 'new string')
WHERE `myField` REGEXP '(?i)my insensitive string'
The option (?i)
makes all the subsequent matches case insensitive (if put at the beginning of the pattern like I have then it all is insensitive).选项
(?i)
使所有后续匹配不区分大小写(如果像我一样放在模式的开头,那么它都是不敏感的)。
See here for more information: https://mariadb.com/kb/en/mariadb/pcre/有关更多信息,请参见此处: https : //mariadb.com/kb/en/mariadb/pcre/
Edit: as of MySQL 8.0 you can now use the regexp_replace
function too, see documentation: https://dev.mysql.com/doc/refman/8.0/en/regexp.html编辑:从 MySQL 8.0 开始,您现在也可以使用
regexp_replace
函数,请参阅文档: https : regexp_replace
Alternative function for one spoken by fvox. fvox 所说的替代功能。
DELIMITER |
CREATE FUNCTION case_insensitive_replace ( REPLACE_WHERE text, REPLACE_THIS text, REPLACE_WITH text )
RETURNS text
DETERMINISTIC
BEGIN
DECLARE last_occurency int DEFAULT '1';
IF LCASE(REPLACE_THIS) = LCASE(REPLACE_WITH) OR LENGTH(REPLACE_THIS) < 1 THEN
RETURN REPLACE_WHERE;
END IF;
WHILE Locate( LCASE(REPLACE_THIS), LCASE(REPLACE_WHERE), last_occurency ) > 0 DO
BEGIN
SET last_occurency = Locate(LCASE(REPLACE_THIS), LCASE(REPLACE_WHERE));
SET REPLACE_WHERE = Insert( REPLACE_WHERE, last_occurency, LENGTH(REPLACE_THIS), REPLACE_WITH);
SET last_occurency = last_occurency + LENGTH(REPLACE_WITH);
END;
END WHILE;
RETURN REPLACE_WHERE;
END;
|
DELIMITER ;
Small test:小测试:
SET @str = BINARY 'New York';
SELECT case_insensitive_replace(@str, 'y', 'K');
Answers: New Kork
答案:
New Kork
This modification of Luist's answer allows one to replace the needle with a differently cased version of the needle (two lines change). Luist 的答案的这种修改允许人们用不同套管版本的针替换针(两行更改)。
DELIMITER |
CREATE FUNCTION case_insensitive_replace ( REPLACE_WHERE text, REPLACE_THIS text, REPLACE_WITH text )
RETURNS text
DETERMINISTIC
BEGIN
DECLARE last_occurency int DEFAULT '1';
IF LENGTH(REPLACE_THIS) < 1 THEN
RETURN REPLACE_WHERE;
END IF;
WHILE Locate( LCASE(REPLACE_THIS), LCASE(REPLACE_WHERE), last_occurency ) > 0 DO
BEGIN
SET last_occurency = Locate(LCASE(REPLACE_THIS), LCASE(REPLACE_WHERE), last_occurency);
SET REPLACE_WHERE = Insert( REPLACE_WHERE, last_occurency, LENGTH(REPLACE_THIS), REPLACE_WITH);
SET last_occurency = last_occurency + LENGTH(REPLACE_WITH);
END;
END WHILE;
RETURN REPLACE_WHERE;
END;
|
DELIMITER ;
I went withhttp://pento.net/2009/02/15/case-insensitive-replace-for-mysql/ (in fvox's answer) which performs the case insensitive search with case sensitive replacement and without changing the case of what should be unaffected characters elsewhere in the searched string.我使用了http://pento.net/2009/02/15/case-insensitive-replace-for-mysql/ (在 fvox 的回答中),它使用区分大小写的替换来执行不区分大小写的搜索,并且不改变应该的大小写是搜索字符串中其他地方不受影响的字符。
NB the comment further down that same page stating that CHAR(255) should be changed to VARCHAR(255) - this seemed to be required for me as well.注意同一页面下方的评论指出 CHAR(255) 应该更改为 VARCHAR(255) - 这似乎也是我所需要的。
In the previous answers, and the pento.net link, the arguments to LOCATE()
are lower-cased.在前面的答案和 pento.net 链接中,
LOCATE()
的参数是小写的。
This is a waste of resources, as LOCATE is case-insensitive by default:这是一种资源浪费,因为 LOCATE 默认不区分大小写:
mysql> select locate('el', 'HELLo');
+-----------------------+
| locate('el', 'HELLo') |
+-----------------------+
| 2 |
+-----------------------+
You can replace你可以更换
WHILE Locate( LCASE(REPLACE_THIS), LCASE(REPLACE_WHERE), last_occurency ) > 0 DO
with和
WHILE Locate(REPLACE_THIS, REPLACE_WHERE, last_occurency ) > 0 DO
etc.等等。
In case of 'special' characters there is unexpected behaviour:如果出现“特殊”字符,则会出现意外行为:
SELECT case_insensitive_replace('A', 'Ã', 'a')
Gives给
a
Which is unexpected... since we only want to replace the à not A这是出乎意料的......因为我们只想替换 à 而不是 A
What is even more weird:更奇怪的是:
SELECT LOCATE('Ã', 'A');
gives给
0
Which is the correct result... seems to have to do with encoding of the parameters of the stored procedure...哪个是正确的结果……似乎与存储过程参数的编码有关……
I like to use a search and replace function I created when I need to replace without worrying about the case of the original or search strings.我喜欢在需要替换时使用我创建的搜索和替换功能,而不用担心原始或搜索字符串的大小写。 This routine bails out quickly if you pass in an empty/null search string or a null replace string without altering the incoming string.
如果您传入空/空搜索字符串或空替换字符串而不更改传入字符串,则此例程会迅速退出。 I also added a safe count down just in case somehow the search keep looping.
我还添加了一个安全倒计时,以防万一搜索继续循环。 This way we don't get stuck in a loop forever.
这样我们就不会永远陷入循环中。 Alter the starting number if you think it is too low.
如果您认为起始编号太低,请更改起始编号。
delimiter //
DROP FUNCTION IF EXISTS `replace_nocase`//
CREATE FUNCTION `replace_nocase`(raw text, find_str varchar(1000), replace_str varchar(1000)) RETURNS text
CHARACTER SET utf8
DETERMINISTIC
BEGIN
declare ret text;
declare len int;
declare hit int;
declare safe int;
if find_str is null or find_str='' or replace_str is null then
return raw;
end if;
set safe=10000;
set ret=raw;
set len=length(find_str);
set hit=LOCATE(find_str,ret);
while hit>0 and safe>0 do
set ret=concat(substring(ret,1,hit-1),replace_str,substring(ret,hit+len));
set hit=LOCATE(find_str,ret,hit+1);
set safe=safe-1;
end while;
return ret;
END//
This question is a bit old but I ran into the same problem and the answers given didn't allow me to solve it entirely.这个问题有点老了,但我遇到了同样的问题,给出的答案并没有让我完全解决它。
I wanted the result to retain the case of the original string.我希望结果保留原始字符串的大小写。
So I made a small modification to the replace_ci
function proposed by fvox :所以我对 fvox 提出的
replace_ci
函数做了一个小修改:
DELIMITER $$
DROP FUNCTION IF EXISTS `replace_ci`$$
CREATE FUNCTION `replace_ci` (str TEXT, needle CHAR(255), str_rep CHAR(255))
RETURNS TEXT
DETERMINISTIC
BEGIN
DECLARE return_str TEXT DEFAULT '';
DECLARE lower_str TEXT;
DECLARE lower_needle TEXT;
DECLARE tmp_needle TEXT;
DECLARE str_origin_char CHAR(1);
DECLARE str_rep_char CHAR(1);
DECLARE final_str_rep TEXT DEFAULT '';
DECLARE pos INT DEFAULT 1;
DECLARE old_pos INT DEFAULT 1;
DECLARE needle_pos INT DEFAULT 1;
IF needle = '' THEN
RETURN str;
END IF;
SELECT LOWER(str) INTO lower_str;
SELECT LOWER(needle) INTO lower_needle;
SELECT LOCATE(lower_needle, lower_str, pos) INTO pos;
WHILE pos > 0 DO
SELECT substr(str, pos, char_length(needle)) INTO tmp_needle;
SELECT '' INTO final_str_rep;
SELECT 1 INTO needle_pos;
WHILE needle_pos <= char_length(tmp_needle) DO
SELECT substr(tmp_needle, needle_pos, 1) INTO str_origin_char;
SELECT SUBSTR(str_rep, needle_pos, 1) INTO str_rep_char;
SELECT CONCAT(final_str_rep, IF(BINARY str_origin_char = LOWER(str_origin_char), LOWER(str_rep_char), IF(BINARY str_origin_char = UPPER(str_origin_char), UPPER(str_rep_char), str_rep_char))) INTO final_str_rep;
SELECT (needle_pos + 1) INTO needle_pos;
END WHILE;
SELECT CONCAT(return_str, SUBSTR(str, old_pos, pos - old_pos), final_str_rep) INTO return_str;
SELECT pos + CHAR_LENGTH(needle) INTO pos;
SELECT pos INTO old_pos;
SELECT LOCATE(lower_needle, lower_str, pos) INTO pos;
END WHILE;
SELECT CONCAT(return_str, SUBSTR(str, old_pos, CHAR_LENGTH(str))) INTO return_str;
RETURN return_str;
END$$
DELIMITER ;
Example of use :使用示例:
SELECT replace_ci( 'MySQL', 'm', 'e' ) as replaced;
Will return : |将返回:| replaced |
替换| |
| --- |
--- | |
| EySQL |
EySQL |
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.