MySQL 查詢樹結構方式
MySQL 查詢樹結構
1. 關於樹結構
此類結構的數據,通常需要表結構中含有id 、parentId等自關聯字段,有時為瞭提高查詢效率還可增加更多冗餘字段,如index,index的值為所有父級目錄的id字符串集合。
關於樹結構數據的組裝,常見的寫法是在程序中通過遞歸的方式去構建出一顆完整的樹,單純通過sql的方式其實並不常用,下面分別給出兩種方式的例子。
2. MySQL自定義函數的方式
什麼是MySQL自定義函數:聚合函數,日期函數之類的都是MySQL的函數,此處我們定義的函數可同他們一樣使用,不過隻能在定義的數據庫中使用,自定義函數和存儲過程類似,不同的是,函數隻會返回一個值,不允許返回一個結果集。
2.1 創建測試數據
CREATE TABLE `tree` ( `id` bigint(11) NOT NULL, `pid` bigint(11) NULL DEFAULT NULL, `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; INSERT INTO `tree` VALUES (1, 0, '中國'); INSERT INTO `tree` VALUES (2, 1, '四川省'); INSERT INTO `tree` VALUES (3, 2, '成都市'); INSERT INTO `tree` VALUES (4, 3, '武侯區'); INSERT INTO `tree` VALUES (5, 4, '紅牌樓'); INSERT INTO `tree` VALUES (6, 1, '廣東省'); INSERT INTO `tree` VALUES (7, 1, '浙江省'); INSERT INTO `tree` VALUES (8, 6, '廣州市');
2.2 獲取 某節點下所有子節點
CREATE FUNCTION `GET_CHILD_NODE`(rootId varchar(100)) RETURNS varchar(2000) BEGIN DECLARE str varchar(2000); DECLARE cid varchar(100); SET str = '$'; SET cid = rootId; WHILE cid is not null DO SET str = concat(str, ',', cid); SELECT group_concat(id) INTO cid FROM tree where FIND_IN_SET(pid, cid); END WHILE; RETURN str; END
調用自定義函數
select * from tree where FIND_IN_SET(id, GET_CHILD_NODE(2));
2.3 獲取 某節點的所有父節點
CREATE FUNCTION `GET_PARENT_NODE`(rootId varchar(100)) RETURNS varchar(1000) BEGIN DECLARE fid varchar(100) default ''; DECLARE str varchar(1000) default rootId; WHILE rootId is not null do SET fid =(SELECT pid FROM tree WHERE id = rootId); IF fid is not null THEN SET str = concat(str, ',', fid); SET rootId = fid; ELSE SET rootId = fid; END IF; END WHILE; return str; END
調用自定義函數
select * from tree where FIND_IN_SET(id, GET_PARENT_NODE(5));
3. Oracle數據庫的方式
隻需要使用start with connect by prior語句即可完成遞歸的樹查詢,詳情請自己查閱相關資料。
4. 程序代碼遞歸的方式構建樹
這裡我就不給出完整代碼瞭,遞歸的方式很簡單,就是先查出所有樹節點,然後通過一個TreeNode類中的add方法遞歸把所有子節點給加進來。核心代碼如下:
public class TreeNodeDTO { private String id; private String parentId; private String name; private List<TreeNodeDTO> children = new ArrayList<>(); public void add(TreeNodeDTO node) { if ("0".equals(node.parentId)) { this.children.add(node); } else if (node.parentId.equals(this.id)) { this.children.add(node); } else { //遞歸調用add()添加子節點 for (TreeNodeDTO tmp_node : children) { tmp_node.add(node); } } } }
5. 通過hashMap,隻需要遍歷一次
就可以完成樹的生成:五星推薦
List<TreeNodeDTO> list = dbMapper.getNodeList(); ArrayList<TreeNodeDTO> rootNodes = new ArrayList<>(); Map<Integer, TreeNodeDTO> map = new HashMap<>(); for (TreeNodeDTO node :list) { map.put(node.getId(), node); Integer parentId = node.getParentId(); // 判斷是否有父節點 (沒有父節點本身就是個父菜單) if (parentId.equals('0')){ rootNodes.add(node); // 找出不是父級菜單的且集合中包括其父菜單ID } else if (map.containsKey(parentId)){ map.get(parentId).getChildren().add(node); } }
MySQL 查詢帶樹狀結構的信息
在Oracle中有函數應用直接能夠查詢出樹狀的樹狀結構信息,例如有下面樹狀結構的組織成員架構,那麼如果我們想查其中一個節點下的所有節點信息
在Oracle中可以直接用下面的語法可以進行直接查詢
START WITH CONNECT BY PRIOR
但是在Mysql中是沒有這個語法的
而如果你也是想要查詢這樣的數據結構信息該怎麼做呢?我們可以自定義函數。我們將上面的信息初始化信息進數據庫中。首先先創建一張表用於存儲這些信息,ID為存儲自身的ID信息,PARENT_ID存儲父ID信息
CREATE TABLE `company_inf` ( `ID` varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL, `NAME` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL, `PARENT_ID` varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL )
然後將圖中的信息初始化表中
INSERT INTO company_inf VALUES ('1','總經理王大麻子','1'); INSERT INTO company_inf VALUES ('2','研發部經理劉大瘸子','1'); INSERT INTO company_inf VALUES ('3','銷售部經理馬二愣子','1'); INSERT INTO company_inf VALUES ('4','財務部經理趙三駝子','1'); INSERT INTO company_inf VALUES ('5','秘書員工J','1'); INSERT INTO company_inf VALUES ('6','研發一組組長吳大棒槌','2'); INSERT INTO company_inf VALUES ('7','研發二組組長鄭老六','2'); INSERT INTO company_inf VALUES ('8','銷售人員G','3'); INSERT INTO company_inf VALUES ('9','銷售人員H','3'); INSERT INTO company_inf VALUES ('10','財務人員I','4'); INSERT INTO company_inf VALUES ('11','開發人員A','6'); INSERT INTO company_inf VALUES ('12','開發人員B','6'); INSERT INTO company_inf VALUES ('13','開發人員C','6'); INSERT INTO company_inf VALUES ('14','開發人員D','7'); INSERT INTO company_inf VALUES ('15','開發人員E','7'); INSERT INTO company_inf VALUES ('16','開發人員F','7');
例如我們想要查詢研發部門經理劉大瘸子下的所有員工,在Oracle中我們可以這樣寫
SELECT * FROM T_PORTAL_AUTHORITY START WITH ID='1' CONNECT BY PRIOR ID = PARENT_ID
而在Mysql中我們需要下面這樣自定義函數
CREATE FUNCTION getChild(parentId VARCHAR(1000)) RETURNS VARCHAR(1000) BEGIN DECLARE oTemp VARCHAR(1000); DECLARE oTempChild VARCHAR(1000); SET oTemp = ''; SET oTempChild =parentId; WHILE oTempChild is not null DO IF oTemp != '' THEN SET oTemp = concat(oTemp,',',oTempChild); ELSE SET oTemp = oTempChild; END IF; SELECT group_concat(ID) INTO oTempChild FROM company_inf where parentId<>ID and FIND_IN_SET(parent_id,oTempChild)>0; END WHILE; RETURN oTemp; END
然後這樣查詢即可
SELECT * FROM company_inf WHERE FIND_IN_SET(ID,getChild('2'));
此時查看查詢出來的信息就是劉大瘸子下所有的員工信息瞭
以上為個人經驗,希望能給大傢一個參考,也希望大傢多多支持WalkonNet。
推薦閱讀:
- Mysql樹形結構的數據庫表設計方案
- Mysql 實現向上遞歸查找父節點並返回樹結構的示例代碼
- 樹形結構數據庫表Schema設計的兩種方案
- MySQL 索引的一些細節分享
- mysql觸發器trigger實例詳解