因標籤過多,在實際的應用過程當中,對標籤表的結構進行了變動。git
從過去的標籤隨意選擇,如今須要對標籤進行分類,簡化選擇難度。添加科目分類以後,須要對歷史上已經被使用過的標籤添加科目信息,進行數據的遷移工做。github
在數據遷移時,使用到了存儲過程,遇到了諸多問題,特此記錄,分享爬坑過程。sql
存儲過程(Stored Procedure
)是一種在數據庫中存儲複雜程序,以便外部程序調用的一種數據庫對象。存儲過程是爲了完成特定功能的
SQL
語句集,經編譯建立並保存在數據庫中,用戶可經過指定存儲過程的名字並給定參數(須要時)來調用執行。數據庫
通俗來講,存儲過程就是數據庫中被編譯的SQL
函數,和普通函數同樣有函數體,有順序結構、條件結構、循環結構,有參數,有輸入輸出。編程
請先閱讀如下兩篇文章,本文在此基礎上對某些細節作了補充。segmentfault
MYSQL如何對數據進行自動化升級--以若是某數據表存在而且某字段不存在時則執行更新操做爲例 - 河北工業大學夢雲智軟件開發團隊數據結構
因定義存儲過程時;
遇到分號會進行語句的執行,須要對;
進行轉義。spa
-- 標識符替換 DELIMITER $$ -- 存儲過程定義 -- 從新定義標識符 DELIMITER ;
-- 存儲過程 CREATE PROCEDURE procedure_name(IN param BIGINT) BEGIN -- 存儲過程具體實現 END$$
請重點注意如下示例代碼中的順序問題,必定是先聲明變量,再聲明遊標,再處理,不然MySQL
會拋錯誤。debug
-- 聲明變量 DECLARE tag_id BIGINT; DECLARE done INT DEFAULT FALSE; -- 聲明遊標 DECLARE cur CURSOR FOR SELECT `id` FROM `tag`; -- 聲明 not found 處理 DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE; OPEN cur; FETCH cur INTO tag_id; WHILE NOT done DO -- 遍歷標籤表 逐條遷移數據 CALL migrate_tag_info_by_id(tag_id); FETCH cur INTO tag_id; END WHILE;
須要重點注意如下代碼:
-- 聲明 not found 處理 DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
去查閱相關博客,聲明NOT FOUND
的處理器是最經常使用的遊標遍歷方式。
TheDECLARE ... HANDLER
statement specifies a handler that deals with one or more conditions. If one of these conditions occurs, the specifiedstatement
executes.statement
can be a simple statement such asSET var_name = value
, or a compound statement written usingBEGIN
andEND
.
DECLARE ... HANDLER
語句指定了處理單條件或多條件的處理器,若是條件知足時,指定的語句會被執行。語句能夠像SET
之類的簡單語句,也能夠是BGGIN ... END
代碼塊。
因此當複雜業務場景下,遊標的嵌套會出問題,這裏我選擇多存儲過程的調用來解決。
須要嵌套的時候,可將內層的遊標放到一個新的存儲過程當中,使用CALL
進行調用。
WHILE NOT done DO -- 遍歷標籤表 逐條遷移數據 CALL migrate_tag_info_by_id(tag_id); FETCH cur INTO tag_id; END WHILE;
編寫存儲過程最難的就是寫了幾十行代碼的存儲過程,執行的時候錯誤不是按行報的,而是一類通用的錯誤,很是難調試。
在查閱相關資料後,MySQL
調試存儲過程須要手動進行日誌的打印。
思路以下:
創建logging
表,編寫一個名爲debug
的存儲過程,功能就是將參數寫入到logging
表中。經過打日誌的方式肯定存儲過程執行狀況。
我這裏遇到了須要查詢SELECT
語句的中間值的問題,解決思路以下:
根據相關的數據結構創建一張臨時表,經過INSERT INTO SELECT
的方式來進行存儲過程執行過程當中相關的中間值的追蹤。
完整示例代碼以下:
-- 標識符替換 DELIMITER $$ -- 存儲過程 根據標籤 id 遷移試題對標籤的引用數據 CREATE PROCEDURE migrate_subject_tag_ref_by_id(IN tag_id BIGINT) BEGIN DECLARE tag_name VARCHAR(255); DECLARE subject_id BIGINT; DECLARE course_id BIGINT; DECLARE tag_ref_id BIGINT; DECLARE done INT DEFAULT FALSE; DECLARE cur CURSOR FOR SELECT `subject`.`id`, `subject`.`course_id` FROM `subject_tags` INNER JOIN `subject` ON `subject_tags`.`subjects_id` = `subject`.`id` WHERE `subject_tags`.`tags_id` = tag_id; DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE; SELECT `name` INTO tag_name FROM `tag` WHERE `id` = tag_id; OPEN cur; FETCH cur INTO subject_id, course_id; WHILE NOT done DO SELECT `id` INTO tag_ref_id FROM `tag` WHERE `tag`.`name` = tag_name AND `tag`.`course_id` = course_id; UPDATE `subject_tags` SET `tags_id` = tag_ref_id WHERE `subjects_id` = subject_id AND `tags_id` = tag_id; FETCH cur INTO subject_id, course_id; END WHILE; END$$ -- 存儲過程 根據標籤 id 遷移新標籤數據 CREATE PROCEDURE migrate_tag_info_by_id(IN tag_id BIGINT) BEGIN DECLARE tag_name VARCHAR(255); DECLARE course_id BIGINT; DECLARE done INT DEFAULT FALSE; DECLARE cur CURSOR FOR SELECT `subject`.`course_id` FROM `subject_tags` INNER JOIN `subject` ON `subject_tags`.`subjects_id` = `subject`.`id` WHERE `subject_tags`.`tags_id` = tag_id GROUP BY `subject`.`course_id`; DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE; SELECT `name` INTO tag_name FROM `tag` WHERE `id` = tag_id; OPEN cur; FETCH cur INTO course_id; WHILE NOT done DO -- 建立新標籤 INSERT INTO `tag`(`name`, `course_id`) VALUES(tag_name, course_id); FETCH cur INTO course_id; END WHILE; -- 遷移對歷史標籤的引用 CALL migrate_subject_tag_ref_by_id(tag_id); -- 刪除歷史標籤 DELETE FROM `tag` WHERE `tag`.`id` = tag_id; END$$ -- 存儲過程 遷移全部標籤數據 CREATE PROCEDURE migrate_all_tag_info() BEGIN DECLARE tag_id BIGINT; DECLARE done INT DEFAULT FALSE; DECLARE cur CURSOR FOR SELECT `id` FROM `tag`; DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE; OPEN cur; FETCH cur INTO tag_id; WHILE NOT done DO -- 遍歷標籤表 逐條遷移數據 CALL migrate_tag_info_by_id(tag_id); FETCH cur INTO tag_id; END WHILE; END$$ -- 從新定義標識符 DELIMITER ; CALL migrate_all_tag_info();
本文做者: 河北工業大學夢雲智開發團隊 - 張喜碩