需求
最近接到的新需求用戶表中有兩個字段分別代表用戶的系統姓名以及真實姓名,系統姓名在系統中是唯一的用於展示給業務操作人員,減少重名對操作人員的干擾,真實姓名用於用戶端展示。但是由於之前需求缺失,並沒有對用戶真實姓名進行維護,所以這次的要求已係統用戶表爲準,去掉其中包含的數字以及字母只保留漢字更新到真實姓名中。
這個功能使用只使用一次,不建議使用Java程序處理,因爲處理之後還需要重新冗餘代碼,所以嘗試從sql角度,用sql將數據修復。
處理流程
1、提取字段中漢字
2、已提取後爲準更新字段
mysql提取漢字主要是按字符循環處理,具體函數如下
DELIMITER $$
DROP FUNCTION IF EXISTS `Num_char`$$
CREATE FUNCTION `Num_char`(Varstring VARCHAR(100)CHARSET utf8) RETURNS VARCHAR(50) CHARSET utf8
BEGIN
DECLARE len INT DEFAULT 0;
DECLARE Tmp VARCHAR(100) DEFAULT '';
SET len=CHAR_LENGTH(Varstring);
WHILE len > 0 DO
IF NOT (MID(Varstring,len,1)REGEXP '^[u0391-uFFE5]')
THEN
SET Tmp=CONCAT(Tmp,MID(Varstring,len,1));
END IF;
SET len = len - 1;
END WHILE;
RETURN REVERSE(Tmp);
END$$
DELIMITER ;
之後利用Num_char函數直接更新表中字段
注意:mysql中不允許一條sql中表中既有查詢又更新,所以把查詢做成子表,然後在去更新
UPDATE tb_student student,
( SELECT Num_char ( NAME) studentName, biz_id FROM tb_student WHERE deleted = 0 ) a
SET student.real_name = a.studentName
WHERE
student.biz_id = a.biz_id
AND student.deleted =0;
由於用戶表中數據比較大,造成此更新語句特別慢,如果線上執行可能會影響正常業務,所以接下主要是爲了提高處理速度。這個更新語句過慢的原因在於Num_char函數,是每條記錄中的每個字符都要比較一下,所以造成非常耗時。但是使用sql提取漢字並沒有其他的好的方案,只能嘗試着從數據上分析,結合業務使用反饋以及數據庫數據查看,發現系統名字中函數數字和字母比例不是很高,所以嘗試從拆分數據,分開更新,如果只有小部分數據使用Num_char函數,那麼速度上也會有很大提高。
更新拆分方案
1、查詢系統名字中含有數字和字母的數據,並進行更新,此次利用正則表達式進行配匹
UPDATE tb_student student,
( SELECT Num_char ( `name`) studentName, biz_id FROM tb_student where name REGEXP '[0-9|A-Za-z]+') a
SET student.real_name = a.studentName
WHERE
student.biz_id = a.biz_id;
2、將剩餘數據的系統名稱直接更新到真實姓名上
UPDATE tb_student student,
( SELECT name studentName, biz_id FROM tb_student where name not REGEXP '[0-9|A-Za-z]+') a
SET student.real_name = a.studentName
WHERE
student.biz_id = a.biz_id;
經過拆分後,時間從1h+已經降到10min以內,處於可以接受範圍中了。
題外話
再提供另外一種思路
以時間爲分割線利用臨時表將需要處理的字段提前在臨時表中處理好,待需求上線後,將臨時表和用戶表做聯合更新,以後利用Num_char函數處理臨時表中之外的數據,同樣能達到相同的效果。(ps:結合目前系統的表數據量考慮第一種方案處理起來會更簡單,大家結合自己的數據合理選擇)
總結
遇到問題解決問題,生活和工作不是考試題目,不是每一個題目都只有一個答案,只要我們願意去想去做,一定會有更好的方案以及新的驚喜等待我們。各位加油幹吧!
ps:mysql字段中根據需要篩選漢字,數字,字符函數如下
DELIMITER $$
DROP FUNCTION IF EXISTS `Num_char_extract`$$
CREATE FUNCTION `Num_char_extract`(Varstring VARCHAR(100)CHARSET utf8, flag INT) RETURNS VARCHAR(50) CHARSET utf8
BEGIN
DECLARE len INT DEFAULT 0;
DECLARE Tmp VARCHAR(100) DEFAULT '';
SET len=CHAR_LENGTH(Varstring);
IF flag = 0
THEN
WHILE len > 0 DO
IF MID(Varstring,len,1)REGEXP'[0-9]' THEN
SET Tmp=CONCAT(Tmp,MID(Varstring,len,1));
END IF;
SET len = len - 1;
END WHILE;
ELSEIF flag=1
THEN
WHILE len > 0 DO
IF (MID(Varstring,len,1)REGEXP '[a-zA-Z]')
THEN
SET Tmp=CONCAT(Tmp,MID(Varstring,len,1));
END IF;
SET len = len - 1;
END WHILE;
ELSEIF flag=2
THEN
WHILE len > 0 DO
IF ( (MID(Varstring,len,1)REGEXP'[0-9]')
OR (MID(Varstring,len,1)REGEXP '[a-zA-Z]') )
THEN
SET Tmp=CONCAT(Tmp,MID(Varstring,len,1));
END IF;
SET len = len - 1;
END WHILE;
ELSEIF flag=3
THEN
WHILE len > 0 DO
IF NOT (MID(Varstring,len,1)REGEXP '^[u0391-uFFE5]')
THEN
SET Tmp=CONCAT(Tmp,MID(Varstring,len,1));
END IF;
SET len = len - 1;
END WHILE;
ELSE
SET Tmp = 'Error: The second paramter should be in (0,1,2,3)';
RETURN Tmp;
END IF;
RETURN REVERSE(Tmp);
END$$
DELIMITER ;
使用方式 標識 0 提取數字 1 提取字母 2提取數字+字母 3 提取漢字
select Num_char_extract('字段',標識)