[英]Case-insensitive REPLACE in MySQL?
MySQL 在默認排序規則下運行幾乎所有字符串比較......除了REPLACE
命令。 我有一個不區分大小寫的排序規則,需要運行一個不區分大小寫的REPLACE
。 有沒有辦法強制REPLACE
使用當前排序規則而不是總是進行區分大小寫的比較? 我願意升級我的 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())
不起作用,則需要創建另一個函數。
我的 2 美分。
由於許多人已從 MySQL 升級到 MariaDB,這些人將擁有一個名為REGEXP_REPLACE
的新函數。 像正常替換一樣使用它,但模式是一個正則表達式。
這是一個工作示例:
UPDATE `myTable`
SET `myField` = REGEXP_REPLACE(`myField`, '(?i)my insensitive string', 'new string')
WHERE `myField` REGEXP '(?i)my insensitive string'
選項(?i)
使所有后續匹配不區分大小寫(如果像我一樣放在模式的開頭,那么它都是不敏感的)。
有關更多信息,請參見此處: https : //mariadb.com/kb/en/mariadb/pcre/
編輯:從 MySQL 8.0 開始,您現在也可以使用regexp_replace
函數,請參閱文檔: https : regexp_replace
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 ;
小測試:
SET @str = BINARY 'New York';
SELECT case_insensitive_replace(@str, 'y', 'K');
答案: New Kork
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 ;
我使用了http://pento.net/2009/02/15/case-insensitive-replace-for-mysql/ (在 fvox 的回答中),它使用區分大小寫的替換來執行不區分大小寫的搜索,並且不改變應該的大小寫是搜索字符串中其他地方不受影響的字符。
注意同一頁面下方的評論指出 CHAR(255) 應該更改為 VARCHAR(255) - 這似乎也是我所需要的。
在前面的答案和 pento.net 鏈接中, LOCATE()
的參數是小寫的。
這是一種資源浪費,因為 LOCATE 默認不區分大小寫:
mysql> select locate('el', 'HELLo');
+-----------------------+
| locate('el', 'HELLo') |
+-----------------------+
| 2 |
+-----------------------+
你可以更換
WHILE Locate( LCASE(REPLACE_THIS), LCASE(REPLACE_WHERE), last_occurency ) > 0 DO
和
WHILE Locate(REPLACE_THIS, REPLACE_WHERE, last_occurency ) > 0 DO
等等。
如果出現“特殊”字符,則會出現意外行為:
SELECT case_insensitive_replace('A', 'Ã', 'a')
給
a
這是出乎意料的......因為我們只想替換 Ã 而不是 A
更奇怪的是:
SELECT LOCATE('Ã', 'A');
給
0
哪個是正確的結果……似乎與存儲過程參數的編碼有關……
我喜歡在需要替換時使用我創建的搜索和替換功能,而不用擔心原始或搜索字符串的大小寫。 如果您傳入空/空搜索字符串或空替換字符串而不更改傳入字符串,則此例程會迅速退出。 我還添加了一個安全倒計時,以防萬一搜索繼續循環。 這樣我們就不會永遠陷入循環中。 如果您認為起始編號太低,請更改起始編號。
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//
這個問題有點老了,但我遇到了同樣的問題,給出的答案並沒有讓我完全解決它。
我希望結果保留原始字符串的大小寫。
所以我對 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 ;
使用示例:
SELECT replace_ci( 'MySQL', 'm', 'e' ) as replaced;
將返回:| 替換| | --- | | EySQL |
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.