日常開發中我們經常會遇到樹形結構數據處理,一般表結構通常會常用id,pid這種設計方案。
之前用oracle、sqlServer數據庫,用相應的語法即可獲取樹形結構數據(oracel:connect by prior ;sqlServer:with...as ...)。
最近一個項目數據庫用的是mysql,需求中含有獲取樹形結構數據的接口,由於之前沒怎么用過mysql,於是第一時間就是查看mysql語法,看看有沒有類似於oracle或sqlserver的遞歸語法,結果是沒有,后來決定自定義數據庫函數(暫時解決了需求)。
1.自定義遞歸函數
SET FOREIGN_KEY_CHECKS=0; -- ---------------------------- -- Function structure for `getPartyChildOrg` -- ---------------------------- DROP FUNCTION IF EXISTS `getPartyChildOrg`; DELIMITER ;; CREATE DEFINER=`root`@`%` FUNCTION `getPartyChildOrg`(orgid BIGINT) RETURNS varchar(4000) CHARSET utf8 BEGIN DECLARE oTemp VARCHAR(4000); DECLARE oTempChild VARCHAR(4000); SET oTemp = ''; SET oTempChild = CAST(orgid AS CHAR); WHILE oTempChild IS NOT NULL DO SET oTemp = CONCAT(oTemp,',',oTempChild); SELECT GROUP_CONCAT(id) INTO oTempChild FROM sub_party_orginfo WHERE logic_delete = 0 and FIND_IN_SET(pid,oTempChild) > 0; END WHILE; RETURN oTemp; END ;; DELIMITER ;
暫時滿足了需求,后期數據量大了,再加上主鍵生成策略生成的主鍵長度比較長,函數返回結果長度受VARCHAR最大長度限制,該方案會失效,導致查詢的數據不全。
2.自定義存儲過程,將查詢的結果存放到臨時表中。
SET FOREIGN_KEY_CHECKS=0; -- ---------------------------- -- Procedure structure for `findPartyOrgChildList` -- ---------------------------- DROP PROCEDURE IF EXISTS `findPartyOrgChildList`; DELIMITER ;; CREATE DEFINER=`root`@`%` PROCEDURE `findPartyOrgChildList`(IN pid VARCHAR(20)) BEGIN DECLARE v_org BIGINT(20); DECLARE done INTEGER DEFAULT 0; -- 查詢結果放入游標中 DECLARE C_org CURSOR FOR SELECT d.id FROM sub_party_orginfo d WHERE d.pid = pid; DECLARE CONTINUE HANDLER FOR NOT found SET done=1; SET @@max_sp_recursion_depth = 10; -- 傳入的組織id寫入臨時表 INSERT INTO temp_party_org VALUES (pid); OPEN C_org; FETCH C_org INTO v_org; WHILE (done=0) DO -- 遞歸調用,查找下級 CALL findPartyOrgChildList(v_org); FETCH C_org INTO v_org; END WHILE; CLOSE C_org; END ;; DELIMITER ; -- ---------------------------- -- Procedure structure for `findPartyOrgList` -- ---------------------------- DROP PROCEDURE IF EXISTS `findPartyOrgList`; DELIMITER ;; CREATE DEFINER=`root`@`%` PROCEDURE `findPartyOrgList`(IN pid BIGINT(20)) BEGIN DROP TEMPORARY TABLE IF EXISTS temp_party_org; -- 創建臨時表 CREATE TEMPORARY TABLE temp_party_org(id BIGINT(20)); -- 清空臨時表數據 DELETE FROM temp_party_org; -- 發起調用 CALL findPartyOrgChildList(pid); END ;; DELIMITER ;