create table tb_menucyb(
id number(10) not null, --主鍵id
title varchar2(50), --標題
parent number(10) --parent id
)
select * from tb_menucybsql
--父菜單
insert into tb_menucyb(id, title, parent) values(1, '父菜單1',0);
insert into tb_menucyb(id, title, parent) values(2, '父菜單2',0);
insert into tb_menucyb(id, title, parent) values(3, '父菜單3',0);
insert into tb_menucyb(id, title, parent) values(4, '父菜單4',0);
insert into tb_menucyb(id, title, parent) values(5, '父菜單5',0);數據庫
--一級菜單
insert into tb_menucyb(id, title, parent) values(6, '一級菜單6',1);
insert into tb_menucyb(id, title, parent) values(7, '一級菜單7',1);
insert into tb_menucyb(id, title, parent) values(8, '一級菜單8',1);
insert into tb_menucyb(id, title, parent) values(9, '一級菜單9',2);
insert into tb_menucyb(id, title, parent) values(10, '一級菜單10',2);
insert into tb_menucyb(id, title, parent) values(11, '一級菜單11',2);
insert into tb_menucyb(id, title, parent) values(12, '一級菜單12',3);
insert into tb_menucyb(id, title, parent) values(13, '一級菜單13',3);
insert into tb_menucyb(id, title, parent) values(14, '一級菜單14',3);
insert into tb_menucyb(id, title, parent) values(15, '一級菜單15',4);
insert into tb_menucyb(id, title, parent) values(16, '一級菜單16',4);
insert into tb_menucyb(id, title, parent) values(17, '一級菜單17',4);
insert into tb_menucyb(id, title, parent) values(18, '一級菜單18',5);
insert into tb_menucyb(id, title, parent) values(19, '一級菜單19',5);
insert into tb_menucyb(id, title, parent) values(20, '一級菜單20',5);oracle
--二級菜單
insert into tb_menucyb(id, title, parent) values(21, '二級菜單21',6);
insert into tb_menucyb(id, title, parent) values(22, '二級菜單22',6);
insert into tb_menucyb(id, title, parent) values(23, '二級菜單23',7);
insert into tb_menucyb(id, title, parent) values(24, '二級菜單24',7);
insert into tb_menucyb(id, title, parent) values(25, '二級菜單25',8);
insert into tb_menucyb(id, title, parent) values(26, '二級菜單26',9);
insert into tb_menucyb(id, title, parent) values(27, '二級菜單27',10);
insert into tb_menucyb(id, title, parent) values(28, '二級菜單28',11);
insert into tb_menucyb(id, title, parent) values(29, '二級菜單29',12);
insert into tb_menucyb(id, title, parent) values(30, '二級菜單30',13);
insert into tb_menucyb(id, title, parent) values(31, '二級菜單31',14);
insert into tb_menucyb(id, title, parent) values(32, '二級菜單32',15);
insert into tb_menucyb(id, title, parent) values(33, '二級菜單33',16);
insert into tb_menucyb(id, title, parent) values(34, '二級菜單34',17);
insert into tb_menucyb(id, title, parent) values(35, '二級菜單35',18);
insert into tb_menucyb(id, title, parent) values(36, '二級菜單36',19);
insert into tb_menucyb(id, title, parent) values(37, '二級菜單37',20);函數
--三級菜單
insert into tb_menucyb(id, title, parent) values(38, '三級菜單38',21);
insert into tb_menucyb(id, title, parent) values(39, '三級菜單39',22);
insert into tb_menucyb(id, title, parent) values(40, '三級菜單40',23);
insert into tb_menucyb(id, title, parent) values(41, '三級菜單41',24);
insert into tb_menucyb(id, title, parent) values(42, '三級菜單42',25);
insert into tb_menucyb(id, title, parent) values(43, '三級菜單43',26);
insert into tb_menucyb(id, title, parent) values(44, '三級菜單44',27);
insert into tb_menucyb(id, title, parent) values(45, '三級菜單45',28);
insert into tb_menucyb(id, title, parent) values(46, '三級菜單46',28);
insert into tb_menucyb(id, title, parent) values(47, '三級菜單47',29);
insert into tb_menucyb(id, title, parent) values(48, '三級菜單48',30);
insert into tb_menucyb(id, title, parent) values(49, '三級菜單49',31);
insert into tb_menucyb(id, title, parent) values(50, '三級菜單50',31);
commit;設計
一、查找一個節點的全部直屬子節點(全部後代)。字符串
select * from tb_menucyb m start with m.id=1 connect by m.parent=prior m.id;it
1 1 父菜單1 0
2 6 一級菜單6 1
3 21 二級菜單21 6
4 38 三級菜單38 21
5 22 二級菜單22 6
6 39 三級菜單39 22
7 7 一級菜單7 1
8 23 二級菜單23 7
9 40 三級菜單40 23
10 24 二級菜單24 7
11 41 三級菜單41 24
12 8 一級菜單8 1
13 25 二級菜單25 8
14 42 三級菜單42 25io
這個查找的是id爲1的節點下的全部直屬子類節點,包括子輩的和孫子輩的全部直屬節點。table
二、 查找一個節點的全部直屬父節點(祖宗)。
select * from tb_menucyb m start with m.id=38 connect by prior m.parent=m.id;效率
38 三級菜單38 21
21 二級菜單21 6
6 一級菜單6 1
1 父菜單1 0
這裏查找的就是id爲1的全部直屬父節點,打個比方就是找到一我的的父親、祖父等。可是值得注意的是這個查詢出來的結果的順序是先列出子類節點再列出父類節點,姑且認爲是個倒序吧。
這兩條語句之間的區別在於prior關鍵字的位置不一樣,因此決定了查詢的方式不一樣。 當parent = prior id時,數據庫會根據當前的id迭代出parent與該id相同的記錄,因此查詢的結果是迭代出了全部的子類記錄;而prior parent = id時,數據庫會跟據當前的parent來迭代出與當前的parent相同的id的記錄,因此查詢出來的結果就是全部的父類結果。
三、
with tmp as(
select a.*, level leaf
from tb_menucyb a
start with a.parent =0
connect by a.parent = prior a.id)
select *
from tmp
where leaf = (select leaf from tmp where id = 50);
查詢與一個節點同級的節點(族兄弟)。 若是在表中設置了級別的字段,那麼在作這類查詢時會很輕鬆,同一級別的就是與那個節點同級的,在這裏列出不使用該字段時的實現
38 三級菜單38 21 4
39 三級菜單39 22 4
40 三級菜單40 23 4
41 三級菜單41 24 4
42 三級菜單42 25 4
43 三級菜單43 26 4
44 三級菜單44 27 4
45 三級菜單45 28 4
46 三級菜單46 28 4
47 三級菜單47 29 4
48 三級菜單48 30 4
49 三級菜單49 31 4
50 三級菜單50 31 4
這裏使用兩個技巧,一個是使用了level來標識每一個節點在表中的級別,還有就是使用with語法模擬出了一張帶有級別的臨時表。
四、 查詢一個節點的父節點的的兄弟節點(伯父與叔父)。
with tmp as(
select tb_menucyb.*, level lev
from tb_menucyb
start with parent =0
connect by parent = prior id)
select b.*
from tmp b,(select *
from tmp
where id = 21 and lev = 2) a
where b.lev = 1
union all
select *
from tmp
where parent = (select distinct x.id
from tmp x, --祖父
tmp y, --父親
(select *
from tmp
where id = 21 and lev > 2) z --兒子
where y.id = z.parent and x.id = y.parent);
6 一級菜單6 1 2
7 一級菜單7 1 2
8 一級菜單8 1 2
這裏查詢分紅如下幾步。
首先,將全表都使用臨時表加上級別;
其次,根據級別來判斷有幾種類型,有三種狀況:
(1)當前節點爲頂級節點,即查詢出來的lev值爲1,那麼它沒有上級節點,不予考慮。
(2)當前節點爲2級節點,查詢出來的lev值爲2,那麼就只要保證lev級別爲1的就是其上級節點的兄弟節點。
(3)其它狀況就是3以及以上級別,那麼就要選查詢出來其上級的上級節點(祖父),再來判斷祖父的下級節點都是屬於該節點的上級節點的兄弟節點。
最後,就是使用union將查詢出來的結果進行結合起來,造成結果集。
五、 查詢一個節點的父節點的同級節點
with tmp as(
select a.*, level leaf
from tb_menucyb a
start with a.parent=0
connect by a.parent = prior a.id)
select *
from tmp
where leaf = (select leaf from tmp where id = 6) - 1;
1 父菜單1 0 1
2 父菜單2 0 1
3 父菜單3 0 1
4 父菜單4 0 1
5 父菜單5 0 1
補充一個概念,對於數據庫來講,根節點並不必定是在數據庫中設計的頂級節點,對於數據庫來講,根節點就是start with開始的地方。
6
名稱要列出名稱所有路徑。
這裏常見的有兩種狀況,一種是從頂級列出,直到當前節點的名稱(或者其它屬性);一種是從當 前節點列出,直到頂級節點的名稱(或其它屬性)。舉地址爲例:國內的習慣是從省開始、到市、到縣、到居委會的,而國外的習慣正好相反。
從頂部開始:
select sys_connect_by_path (title, '/')
from tb_menucyb
where id = 50
start with parent =0
connect by parent = prior id;
1 /父菜單3/一級菜單14/二級菜單31/三級菜單50
七、 從當前節點開始:
select sys_connect_by_path (title, '/')
from tb_menucyb
start with id = 50
connect by prior parent = id;
/三級菜單50
/三級菜單50/二級菜單31
/三級菜單50/二級菜單31/一級菜單14
/三級菜單50/二級菜單31/一級菜單14/父菜單3
oracle只提供了一個sys_connect_by_path函數,卻忘了字符串的鏈接的順序。在上面的例子中,第一個sql是從根節點開始遍歷,而 第二個sql是直接找到當前節點,從效率上來講已是千差萬別,更關鍵的是第一個sql只能選擇一個節點,而第二個sql倒是遍歷出了一顆樹來。
八、 列出當前節點的根節點。
在前面說過,根節點就是start with開始的地方。
select connect_by_root title, tb_menucyb.*
from tb_menucyb
start with id = 50
connect by prior parent = id;
三級菜單50 50 三級菜單50 31
三級菜單50 31 二級菜單31 14
三級菜單50 14 一級菜單14 3
三級菜單50 3 父菜單3 0
connect_by_root函數用來列的前面,記錄的是當前節點的根節點的內容。
九、列出當前節點是否爲葉子。
這個比較常見,尤爲在動態目錄中,在查出的內容是否還有下級節點時,這個函數是很適用的。
connect_by_isleaf函數用來判斷當前節點是否包含下級節點,若是包含的話,說明不是葉子節點,這裏返回0;反之,若是不包含下級節點,這裏返回1。
0 1 父菜單1 0 0 6 一級菜單6 1 0 21 二級菜單21 6 1 38 三級菜單38 21 0 22 二級菜單22 6 1 39 三級菜單39 22 0 7 一級菜單7 1 0 23 二級菜單23 7 1 40 三級菜單40 23 0 24 二級菜單24 7 1 41 三級菜單41 24 0 8 一級菜單8 1 0 25 二級菜單25 8 1 42 三級菜單42 25 0 2 父菜單2 0 0 9 一級菜單9 2 0 26 二級菜單26 9 1 43 三級菜單43 26 0 10 一級菜單10 2 0 27 二級菜單27 10 1 44 三級菜單44 27 0 11 一級菜單11 2 0 28 二級菜單28 11 1 45 三級菜單45 28 1 46 三級菜單46 28 0 3 父菜單3 0 0 12 一級菜單12 3 0 29 二級菜單29 12 1 47 三級菜單47 29 0 13 一級菜單13 3 0 30 二級菜單30 13 1 48 三級菜單48 30 0 14 一級菜單14 3 0 31 二級菜單31 14 1 49 三級菜單49 31 1 50 三級菜單50 31 0 4 父菜單4 0 0 15 一級菜單15 4 1 32 二級菜單32 15 0 16 一級菜單16 4 1 33 二級菜單33 16 0 17 一級菜單17 4 1 34 二級菜單34 17 0 5 父菜單5 0 0 18 一級菜單18 5 1 35 二級菜單35 18 0 19 一級菜單19 5 1 36 二級菜單36 19 0 20 一級菜單20 5 1 37 二級菜單37 20