1. 程式人生 > >Sql遞歸查詢

Sql遞歸查詢

優化 value 內容 說明 其他 結構 菜單 等價 path

/*Sql遞歸查詢*/
    /*
    實際就是把所有樹的節點查找出來
    Oracle的一個表中也可以保存樹形結構信息,用start with...connect by等關鍵字
    eg:創建表並插入數據
    */
    Create table Tree(son char(10),father char(10));
    insert into tree (SON, FATHER) values (孫子1, 兒子);
    insert into tree (SON, FATHER) values (孫子2, 兒子);
    insert into tree (SON, FATHER) values
(兒子, 父親); insert into tree (SON, FATHER) values (父親, 祖父); /* son father 孫子1 兒子 孫子2 兒子 兒子 父親 父親 祖父 數據結構是: 祖父 | 父親 | 兒子 / 孫子1 孫子2
*/ --查詢以祖父為根節點的所有節點值 SELECT son from Tree START WITH father=祖父 CONNECT BY PRIOR son=father /* START WITH後的內容可以看成一個WHERE的限制條件,其中START WITH 是指定樹的根 ,還可以指定多個根,比如father in (‘祖父‘,‘父親‘); CONNECTION BY PRIOR son=father相當於表明在遞歸過程中,查找到的樹種其他節點接著 又作為根結點,然後繼續遞歸。 只要記住 CONNECTION BY PRIOR那一行,要查詢的列名放在前,根列名放等號後
*/ --例: --樹形菜單結構 CREATE TABLE tb_menu( id number(10) not null,--主鍵id title varchar(50),--標題 parent number(10) --父菜單id ); --父菜單數據 insert into tb_menu(id,title,parent) values(1,父菜單1,0); insert into tb_menu(id,title,parent) values(2,父菜單2,0); insert into tb_menu(id,title,parent) values(3,父菜單3,0); insert into tb_menu(id,title,parent) values(4,父菜單4,0); insert into tb_menu(id,title,parent) values(5,父菜單5,0); --一級子菜單 insert into tb_menu(id,title,parent) values(6,一級子菜單6_1,1); insert into tb_menu(id,title,parent) values(7,一級子菜單7_1,1); insert into tb_menu(id,title,parent) values(8,一級子菜單8_1,1); insert into tb_menu(id,title,parent) values(9,一級子菜單9_2,2); insert into tb_menu(id,title,parent) values(10,一級子菜單10_2,2); insert into tb_menu(id,title,parent) values(11,一級子菜單11_2,2); insert into tb_menu(id,title,parent) values(12,一級子菜單12_3,3); insert into tb_menu(id,title,parent) values(13,一級子菜單13_3,3); insert into tb_menu(id,title,parent) values(14,一級子菜單14_3,3); insert into tb_menu(id,title,parent) values(15,一級子菜單15_4,4); insert into tb_menu(id,title,parent) values(16,一級子菜單16_4,4); insert into tb_menu(id,title,parent) values(17,一級子菜單17_4,4); insert into tb_menu(id,title,parent) values(18,一級子菜單18_5,5); insert into tb_menu(id,title,parent) values(19,一級子菜單19_5,5); insert into tb_menu(id,title,parent) values(20,一級子菜單20_5,5); --二級子菜單 insert into tb_menu(id,title,parent) values(21,二級子菜單21_6,6); insert into tb_menu(id,title,parent) values(22,二級子菜單22_6,6); insert into tb_menu(id,title,parent) values(23,二級子菜單23_7,7); insert into tb_menu(id,title,parent) values(24,二級子菜單24_7,7); insert into tb_menu(id,title,parent) values(25,二級子菜單25_8,8); insert into tb_menu(id,title,parent) values(26,二級子菜單26_9,9); insert into tb_menu(id,title,parent) values(27,二級子菜單27_10,10); insert into tb_menu(id,title,parent) values(28,二級子菜單28_11,11); insert into tb_menu(id,title,parent) values(29,二級子菜單29_12,12); insert into tb_menu(id,title,parent) values(30,二級子菜單30_13,13); insert into tb_menu(id,title,parent) values(31,二級子菜單31_14,14); insert into tb_menu(id,title,parent) values(32,二級子菜單32_15,15); insert into tb_menu(id,title,parent) values(33,二級子菜單33_16,16); insert into tb_menu(id,title,parent) values(34,二級子菜單34_17,17); insert into tb_menu(id,title,parent) values(35,二級子菜單35_18,18); insert into tb_menu(id,title,parent) values(36,二級子菜單36_19,19); insert into tb_menu(id,title,parent) values(37,二級子菜單37_20,20); --三級子菜單 insert into tb_menu(id,title,parent) values(38,三級子菜單38_21,21); insert into tb_menu(id,title,parent) values(39,三級子菜單39_22,22); insert into tb_menu(id,title,parent) values(40,三級子菜單40_23,23); insert into tb_menu(id,title,parent) values(41,三級子菜單41_24,24); insert into tb_menu(id,title,parent) values(42,三級子菜單42_25,25); insert into tb_menu(id,title,parent) values(43,三級子菜單43_26,26); insert into tb_menu(id,title,parent) values(44,三級子菜單44_27,27); insert into tb_menu(id,title,parent) values(45,三級子菜單45_28,28); insert into tb_menu(id,title,parent) values(46,三級子菜單46_28,28); insert into tb_menu(id,title,parent) values(47,三級子菜單47_29,29); insert into tb_menu(id,title,parent) values(48,三級子菜單48_30,30); insert into tb_menu(id,title,parent) values(49,三級子菜單49_31,31); insert into tb_menu(id,title,parent) values(50,三級子菜單50_31,31); /* 數據結構: 父菜單1 | / 一級子菜單1_6 ... ... / 二級子菜單21_6... ... / 三級子菜單38_21... ... ... */ --①查詢表中所有父菜單 select * from tb_menu m where m.parent=0; --②查詢表中所有一級子菜單(即數據結構中的直屬子節點) select * from tb_menu m where m.parent=1; --③查詢父菜單1的所有子孫 select * from tb_menu m start with m.id=1 connect by PRIOR m.id=m.parent /* 這個SQL語句的等價寫法: select * from tb_menu m start with m.id=1 connect by m.parent=PRIOR m.id 可以記做: prior放在那裏,就找誰,即放在id前,即為找id的所有子孫後代 */ --④查詢一個節點的直屬父節點(用不到樹形查詢) select c.id, c.title, p.id as 父節點id , p.title as 父節點名稱 from tb_menu c, tb_menu p where c.parent=p.id and c.id=6 --⑤查詢一個節點的所有父節點,即父親節點,祖先節點(需要使用樹形查詢) select * from tb_menu m start with m.id=50 connect by PRIOR m.parent=m.id /*引入level和lpad實現分層顯示*/ select level, lpad( , 2 * level - 1) || m.id from tb_menu m start with m.id = 1 connect by prior m.id = m.parent /* prior就是表示上一條記錄的意思,即prior m.parent=m.id就是表示上一條記錄的父id是本條記錄的id 也就是本條記錄是上一條記錄的父親,那麽就是在查詢所有記錄的父節點/祖先節點,反之。 */ --⑥查詢一個節點(一級子菜單6_1)的兄弟節點(親兄弟) select * from tb_menu m where exists (select * from tb_menu m2 where m.parent=m2.parent and m2.id=6) /*從SQL語句理解就是:查詢表的數據,數據符合條件,在m2只要存在m.parent=m2.parent的數據,即取出*/ --⑦查詢一個節點所有同級的節點(族兄弟) with tmp as( select a.* ,level leaf from tb_menu a start with a.parent=0 connect by prior a.id=a.parent ) select * from tmp where leaf=(select leaf from tmp where id=50) /*在SQL語句⑦中使用了level來標識每個節點在表中的級別【level表示遞歸的層次,即查詢的深度】,使用了with語法來虛擬出了一張帶有級別的臨時表*/ /*=========================【補充】with語法:================================== 要求Oracle版本Oracle 9i及以上 其實就是把一些重復用到的SQL語句放在with as裏面,取一個別名,後面的查詢中就可以使用這個別名,對大批量的SQL 語句起到一個優化的作用。 eg1: with t1 as (select * from emp e where e.ename like ‘%N%‘) select * from t1; eg2: with t1 as (select * from emp), t2 as (select * from dept) select * from t1,t2 where t1.deptno=t2.deptno 對於union all通常使用with as提升效率 因為union all的每個執行部分可能相同,如果每個部分都執行一遍成本太高,如果使用with as短語只執行一次即可 eg3: with t1 as (select t.name as name from table1 t where t.age>20), t2 as (select s.lastname as name from table2 s where exists(select s.name from table3 p where s.id=p.id and p.id=‘a001‘)) select * from t1 union all select * from t2 =====================================================================*/ --⑧查詢一個節點的父節點的親兄弟節點 /* 1、如果當前節點為最頂級的節點,即level=1,沒有父節點,不考慮 2、如果當期節點為2級節點,即level=2,那麽level=1的節點就是他的父節點和父節點的兄弟節點 3、如果level是3或者3以上,那麽就要查詢出來其祖父節點,再判斷祖父節點的下一級(即父節點)是其父節點和父節點的兄弟節點 4、將結果集union在一起 */ with tmp as ( select m.*,level lev from tb_menu m start with m.parent=0 connect by m.parent= prior m.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 ) --⑨查詢一個節點的父節點的所有兄弟節點(族兄弟) with tmp as ( select m.*,level leaf from tb_menu m start with m.parent=0 connect by m.parent= prior m.id ) select * from tmp where leaf=(select leaf from tmp where id=6)-1 --⑩列出全部路徑,比如省/市/區/街道/社區 /*從頂部開始*/ select sys_connect_by_path(title,/) from tb_menu m where id=50 start with parent =0 connect by parent =prior id /*從當前節點開始*/ select sys_connect_by_path(title,/) from tb_menu m start with id=50 connect by prior parent =id /* sys_connect_by_path函數是從start with開始的地方遍歷,並記錄下遍歷到的節點。start with開始的地方被視為根節點, 將遍歷到的路徑根據函數中的分隔符組成一個新的字符串! */ /* 還有一些功能同樣很強大的函數 connect_by_root函數用在列前,記錄的是當前節點的根節點的內容 select connect_by_root title,tb_menu.* from tb_menu start with id=50 connect by prior parent=id connect_by_isleaf函數用來判斷當前節點是否包含下級節點,如果包含下級節點說明不是葉子節點(即沒有子節點的節點,不管在第幾級,只要沒有子節點就是葉子節點),返回0, 否則不包含,返回1 select connect_by_isleaf,tb_menu.* from tb_menu start with parent=0 connect by parent =prior id; */

Sql遞歸查詢